From 66ee32d0e21537311010bc9ed7f938d9c4ef9f17 Mon Sep 17 00:00:00 2001 From: yjj38 Date: Thu, 17 Jul 2025 11:18:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor(app):=20=E9=87=8D=E6=9E=84=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=AE=89=E8=A3=85=E5=92=8C=E7=AE=A1=E7=90=86=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 AdvertisingIdClient 类 - 新增 AfClient 类,实现信息设置和文件下载功能 - 新增 AppState 类,用于存储应用状态信息 - 新增 AppUtils 类,提供应用安装、卸载、检查等工具方法 --- .../java/com/android/grape/MainActivity.kt | 10 +- .../java/com/android/grape/data/AppState.kt | 139 + .../com/android/grape/job/AutoJobService.kt | 9 +- .../android/grape/job/CheckIpJobService.kt | 25 +- .../grape/job/DownloadAppJobService.kt | 17 +- .../com/android/grape/job/InstallService.kt | 19 +- .../com/android/grape/job/MonitorService.kt | 9 +- .../com/android/grape/job/OpenAppService.kt | 42 +- .../android/grape/job/RecoverJobService.kt | 7 +- .../grape/job/SendCallbackJobService.kt | 141 +- .../grape/job/StartVpnPortJobService.kt | 16 +- .../grape/job/StartVpnServerJobService.kt | 5 +- .../com/android/grape/job/UnInstallService.kt | 21 +- .../android/grape/manager/ConfigManager.kt | 191 + .../android/grape/manager/TrackingManager.kt | 54 + .../java/com/android/grape/net/AfClient.kt | 316 ++ .../main/java/com/android/grape/net/MyPost.kt | 127 - .../android/grape/receiver/ScriptReceiver.kt | 9 +- .../java/com/android/grape/sai/IOUtils.kt | 37 +- .../grape/sai/ShellSaiPackageInstaller.kt | 22 +- .../android/grape/util/AdvertisingIdClient.kt | 119 - .../java/com/android/grape/util/AppUtils.kt | 651 ++++ .../com/android/grape/util/BackupUtils.kt | 378 ++ .../grape/util/ChangeDeviceInfoUtil.kt | 7 +- .../com/android/grape/util/ContextUtils.kt | 35 + .../com/android/grape/util/DeviceUtils.kt | 399 +++ .../java/com/android/grape/util/FileUtils.kt | 230 +- .../java/com/android/grape/util/HookUtils.kt | 66 + .../com/android/grape/util/InstallUtils.kt | 76 + .../java/com/android/grape/util/JsonUtils.kt | 392 +++ .../java/com/android/grape/util/ScriptUtil.kt | 126 - .../com/android/grape/util/ScriptUtils.kt | 170 + .../com/android/grape/util/ServiceUtils.kt | 79 +- .../java/com/android/grape/util/ShellUtils.kt | 765 ++-- .../java/com/android/grape/util/TaskUtils.kt | 544 +++ .../main/java/com/android/grape/util/Util.kt | 3132 ----------------- 36 files changed, 4449 insertions(+), 3936 deletions(-) create mode 100644 app/src/main/java/com/android/grape/data/AppState.kt create mode 100644 app/src/main/java/com/android/grape/manager/ConfigManager.kt create mode 100644 app/src/main/java/com/android/grape/manager/TrackingManager.kt create mode 100644 app/src/main/java/com/android/grape/net/AfClient.kt delete mode 100644 app/src/main/java/com/android/grape/net/MyPost.kt delete mode 100644 app/src/main/java/com/android/grape/util/AdvertisingIdClient.kt create mode 100644 app/src/main/java/com/android/grape/util/AppUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/BackupUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/ContextUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/DeviceUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/HookUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/InstallUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/JsonUtils.kt delete mode 100644 app/src/main/java/com/android/grape/util/ScriptUtil.kt create mode 100644 app/src/main/java/com/android/grape/util/ScriptUtils.kt create mode 100644 app/src/main/java/com/android/grape/util/TaskUtils.kt delete mode 100644 app/src/main/java/com/android/grape/util/Util.kt diff --git a/app/src/main/java/com/android/grape/MainActivity.kt b/app/src/main/java/com/android/grape/MainActivity.kt index f24aaf2..96f0bad 100644 --- a/app/src/main/java/com/android/grape/MainActivity.kt +++ b/app/src/main/java/com/android/grape/MainActivity.kt @@ -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() { diff --git a/app/src/main/java/com/android/grape/data/AppState.kt b/app/src/main/java/com/android/grape/data/AppState.kt new file mode 100644 index 0000000..ea191dc --- /dev/null +++ b/app/src/main/java/com/android/grape/data/AppState.kt @@ -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 + } + +} diff --git a/app/src/main/java/com/android/grape/job/AutoJobService.kt b/app/src/main/java/com/android/grape/job/AutoJobService.kt index 2aeee28..484d9e3 100644 --- a/app/src/main/java/com/android/grape/job/AutoJobService.kt +++ b/app/src/main/java/com/android/grape/job/AutoJobService.kt @@ -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 { diff --git a/app/src/main/java/com/android/grape/job/CheckIpJobService.kt b/app/src/main/java/com/android/grape/job/CheckIpJobService.kt index 0d64d33..5f6898c 100644 --- a/app/src/main/java/com/android/grape/job/CheckIpJobService.kt +++ b/app/src/main/java/com/android/grape/job/CheckIpJobService.kt @@ -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) { @@ -67,4 +72,4 @@ class CheckIpJobService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt b/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt index 8b27a7f..eb2da81 100644 --- a/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt +++ b/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt @@ -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) } } @@ -48,4 +49,4 @@ class DownloadAppJobService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/InstallService.kt b/app/src/main/java/com/android/grape/job/InstallService.kt index 3c211bc..60f2167 100644 --- a/app/src/main/java/com/android/grape/job/InstallService.kt +++ b/app/src/main/java/com/android/grape/job/InstallService.kt @@ -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) } } } @@ -64,4 +67,4 @@ class InstallService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/MonitorService.kt b/app/src/main/java/com/android/grape/job/MonitorService.kt index a98e208..b3c452c 100644 --- a/app/src/main/java/com/android/grape/job/MonitorService.kt +++ b/app/src/main/java/com/android/grape/job/MonitorService.kt @@ -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() diff --git a/app/src/main/java/com/android/grape/job/OpenAppService.kt b/app/src/main/java/com/android/grape/job/OpenAppService.kt index 5027ace..1ae04e2 100644 --- a/app/src/main/java/com/android/grape/job/OpenAppService.kt +++ b/app/src/main/java/com/android/grape/job/OpenAppService.kt @@ -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); } @@ -76,4 +80,4 @@ class OpenAppService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/RecoverJobService.kt b/app/src/main/java/com/android/grape/job/RecoverJobService.kt index 3271525..053f8cb 100644 --- a/app/src/main/java/com/android/grape/job/RecoverJobService.kt +++ b/app/src/main/java/com/android/grape/job/RecoverJobService.kt @@ -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() } @@ -49,4 +50,4 @@ class RecoverJobService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/SendCallbackJobService.kt b/app/src/main/java/com/android/grape/job/SendCallbackJobService.kt index 28a3ea9..fc464a1 100644 --- a/app/src/main/java/com/android/grape/job/SendCallbackJobService.kt +++ b/app/src/main/java/com/android/grape/job/SendCallbackJobService.kt @@ -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 } diff --git a/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt b/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt index 3b8325e..7a17664 100644 --- a/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt +++ b/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt @@ -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") @@ -68,4 +72,4 @@ class StartVpnPortJobService : JobIntentService() { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt b/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt index ff51cfc..c3e2cf0 100644 --- a/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt +++ b/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt @@ -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功能 @@ -51,4 +48,4 @@ class StartVpnServerJobService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/job/UnInstallService.kt b/app/src/main/java/com/android/grape/job/UnInstallService.kt index b642e22..1f32932 100644 --- a/app/src/main/java/com/android/grape/job/UnInstallService.kt +++ b/app/src/main/java/com/android/grape/job/UnInstallService.kt @@ -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 { @@ -33,4 +36,4 @@ class UnInstallService : JobIntentService() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/manager/ConfigManager.kt b/app/src/main/java/com/android/grape/manager/ConfigManager.kt new file mode 100644 index 0000000..0430ae6 --- /dev/null +++ b/app/src/main/java/com/android/grape/manager/ConfigManager.kt @@ -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() + } +} diff --git a/app/src/main/java/com/android/grape/manager/TrackingManager.kt b/app/src/main/java/com/android/grape/manager/TrackingManager.kt new file mode 100644 index 0000000..87afdc5 --- /dev/null +++ b/app/src/main/java/com/android/grape/manager/TrackingManager.kt @@ -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) + } +} diff --git a/app/src/main/java/com/android/grape/net/AfClient.kt b/app/src/main/java/com/android/grape/net/AfClient.kt new file mode 100644 index 0000000..e963849 --- /dev/null +++ b/app/src/main/java/com/android/grape/net/AfClient.kt @@ -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 + } +} diff --git a/app/src/main/java/com/android/grape/net/MyPost.kt b/app/src/main/java/com/android/grape/net/MyPost.kt deleted file mode 100644 index 204e960..0000000 --- a/app/src/main/java/com/android/grape/net/MyPost.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/receiver/ScriptReceiver.kt b/app/src/main/java/com/android/grape/receiver/ScriptReceiver.kt index 9365018..becab19 100644 --- a/app/src/main/java/com/android/grape/receiver/ScriptReceiver.kt +++ b/app/src/main/java/com/android/grape/receiver/ScriptReceiver.kt @@ -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" @@ -25,4 +26,4 @@ class ScriptReceiver : BroadcastReceiver() { 3000 ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/sai/IOUtils.kt b/app/src/main/java/com/android/grape/sai/IOUtils.kt index 7b65354..8b07618 100644 --- a/app/src/main/java/com/android/grape/sai/IOUtils.kt +++ b/app/src/main/java/com/android/grape/sai/IOUtils.kt @@ -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 -> @@ -176,4 +211,4 @@ object IOUtils { return readStream(`in`) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/sai/ShellSaiPackageInstaller.kt b/app/src/main/java/com/android/grape/sai/ShellSaiPackageInstaller.kt index 530e58a..a1c8651 100644 --- a/app/src/main/java/com/android/grape/sai/ShellSaiPackageInstaller.kt +++ b/app/src/main/java/com/android/grape/sai/ShellSaiPackageInstaller.kt @@ -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) } } @@ -355,4 +357,4 @@ abstract class ShellSaiPackageInstaller protected constructor(c: Context?) : companion object { private val mSharedSemaphore = Semaphore(1) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/util/AdvertisingIdClient.kt b/app/src/main/java/com/android/grape/util/AdvertisingIdClient.kt deleted file mode 100644 index 21cf25c..0000000 --- a/app/src/main/java/com/android/grape/util/AdvertisingIdClient.kt +++ /dev/null @@ -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(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 - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/AppUtils.kt b/app/src/main/java/com/android/grape/util/AppUtils.kt new file mode 100644 index 0000000..00fa97e --- /dev/null +++ b/app/src/main/java/com/android/grape/util/AppUtils.kt @@ -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.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.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..>() + val packageInfo: List = 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 + } + +} diff --git a/app/src/main/java/com/android/grape/util/BackupUtils.kt b/app/src/main/java/com/android/grape/util/BackupUtils.kt new file mode 100644 index 0000000..1000ca0 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/BackupUtils.kt @@ -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 + } +} diff --git a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt index 2eeb851..17e9b79 100644 --- a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt +++ b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt @@ -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 { @@ -399,4 +400,4 @@ object ChangeDeviceInfoUtil { // ShellUtils.execRootCmd("setprop ro.build.fingerprint \"Vortex/HD65_Select/HD65_Select:13/TP1A.220624.014/20240306:user/release-keys\"") // ShellUtils.execRootCmd("setprop ro.board.platform sm8150p") // } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/android/grape/util/ContextUtils.kt b/app/src/main/java/com/android/grape/util/ContextUtils.kt new file mode 100644 index 0000000..37b053e --- /dev/null +++ b/app/src/main/java/com/android/grape/util/ContextUtils.kt @@ -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 + } +} diff --git a/app/src/main/java/com/android/grape/util/DeviceUtils.kt b/app/src/main/java/com/android/grape/util/DeviceUtils.kt new file mode 100644 index 0000000..7839f58 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/DeviceUtils.kt @@ -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(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.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) + } + } + } +} diff --git a/app/src/main/java/com/android/grape/util/FileUtils.kt b/app/src/main/java/com/android/grape/util/FileUtils.kt index b983225..4f40ef0 100644 --- a/app/src/main/java/com/android/grape/util/FileUtils.kt +++ b/app/src/main/java/com/android/grape/util/FileUtils.kt @@ -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? = FileUtils.getFilesAllName(path) + if (list != null) { + for (listname in list) { + Log.e("TAG", "listname :$listname") + //判断文件是不是想要查找后缀 + if (listname.endsWith(houzhui)) { + //获取路径下最后一个‘/’后的坐标 + val lastindex = listname.lastIndexOf("/") + //获取具体文件名称 + name = listname.substring(lastindex + 1, listname.length) + //获取到想要的名称后,去干你想干的事 + //dosomething + Log.e("name", name!!) + } + } + } + } + /** * 向文件中添加内容 @@ -405,7 +542,7 @@ object FileUtils { return } } - LogUtils.d("TAG", "writePackageName: $packageName", null) + LogUtils.d("FileUtils", "writePackageName: $packageName", null) try { BufferedOutputStream( FileOutputStream(file) @@ -420,7 +557,7 @@ object FileUtils { } fun writeDevice(packageName: String, device: String) { - LogUtils.d("TAG", "writeDevice: $device") + LogUtils.d("FileUtils", "writeDevice: $device") val base64Content: String = Base64.encodeToString(device.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP) // 构造安全命令 @@ -428,18 +565,89 @@ object FileUtils { val cmd = "sh -c 'printf %s '\''" + base64Content + "'\'' > " + filePath + "'" val result = ShellUtils.execRootCmdAndGetResult(cmd) ShellUtils.execRootCmdAndGetResult("chmod 777 $filePath") - LogUtils.d("TAG", "writeDevice: $result") + LogUtils.d("FileUtils", "writeDevice: $result") } - fun runPlugin(packageName: String){ - val cmd = "apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk" + fun runPlugin(packageName: String) { + val cmd = + "apmt patch add -n ArmCloudAF -p $packageName -f /sdcard/Download/ArmCloudAF_lo.apk" val result = ShellUtils.execRootCmdAndGetResult(cmd) - LogUtils.d("TAG", "runPlugin: $result") + LogUtils.d("FileUtils", "runPlugin: $result") } - fun deletePlugin(){ + fun deletePlugin() { val cmd = "apmt patch del -n ArmCloudAF" val result = ShellUtils.execRootCmdAndGetResult(cmd) - LogUtils.d("TAG", "deletePlugin: $result") + LogUtils.d("FileUtils", "deletePlugin: $result") } -} \ No newline at end of file + + 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 "" + } +} diff --git a/app/src/main/java/com/android/grape/util/HookUtils.kt b/app/src/main/java/com/android/grape/util/HookUtils.kt new file mode 100644 index 0000000..b6b3abc --- /dev/null +++ b/app/src/main/java/com/android/grape/util/HookUtils.kt @@ -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") + } +} diff --git a/app/src/main/java/com/android/grape/util/InstallUtils.kt b/app/src/main/java/com/android/grape/util/InstallUtils.kt new file mode 100644 index 0000000..6ce51e5 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/InstallUtils.kt @@ -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 + } +} diff --git a/app/src/main/java/com/android/grape/util/JsonUtils.kt b/app/src/main/java/com/android/grape/util/JsonUtils.kt new file mode 100644 index 0000000..67f07a8 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/JsonUtils.kt @@ -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) + } + } + +} diff --git a/app/src/main/java/com/android/grape/util/ScriptUtil.kt b/app/src/main/java/com/android/grape/util/ScriptUtil.kt deleted file mode 100644 index a94a355..0000000 --- a/app/src/main/java/com/android/grape/util/ScriptUtil.kt +++ /dev/null @@ -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 - } - } -} diff --git a/app/src/main/java/com/android/grape/util/ScriptUtils.kt b/app/src/main/java/com/android/grape/util/ScriptUtils.kt new file mode 100644 index 0000000..89674de --- /dev/null +++ b/app/src/main/java/com/android/grape/util/ScriptUtils.kt @@ -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 + } + } +} diff --git a/app/src/main/java/com/android/grape/util/ServiceUtils.kt b/app/src/main/java/com/android/grape/util/ServiceUtils.kt index 8bc4b08..33c17a3 100644 --- a/app/src/main/java/com/android/grape/util/ServiceUtils.kt +++ b/app/src/main/java/com/android/grape/util/ServiceUtils.kt @@ -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 { } } } -} \ No newline at end of file + + 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("文件写入", "成功") + } +} diff --git a/app/src/main/java/com/android/grape/util/ShellUtils.kt b/app/src/main/java/com/android/grape/util/ShellUtils.kt index 529c423..194ad4a 100644 --- a/app/src/main/java/com/android/grape/util/ShellUtils.kt +++ b/app/src/main/java/com/android/grape/util/ShellUtils.kt @@ -1,360 +1,593 @@ -package com.android.grape.util; +package com.android.grape.util -import static java.security.AccessController.getContext; +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import com.android.grape.data.AppState.recordPackageName +import com.android.grape.util.AppUtils.getPackageUserID +import com.android.grape.util.ContextUtils.getMonitorDir +import com.android.grape.util.TaskUtils.getRecordListTxtFileName +import com.blankj.utilcode.util.LogUtils +import java.io.BufferedOutputStream +import java.io.BufferedReader +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.InputStreamReader +import java.io.InterruptedIOException +import java.io.PrintWriter +import java.nio.charset.StandardCharsets +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit -import java.io.BufferedOutputStream; -import java.io.BufferedReader; +/** + * @Time: 2025-56-16 14:56 + * @Creator: 初屿贤 + * @File: ShellUtils + * @Project: AndroidGrape + * @Description: + */ +// ShellUtils.kt +object ShellUtils { -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.util.Log; -import com.blankj.utilcode.util.LogUtils; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public class ShellUtils { - - public static String getPackagePath(Context context, String packageName) { + /** + * 递归更改指定目录及其内容的所有权 + * 使用Shell命令给提供的用户和组。 + * + * @param上下文调用该方法的上下文 + * @param dir是将更改所有权的目录的路径 + * @param groupanduser组和用户以“组:用户”的形式 + * @return true如果操作成功,则否则为否则 + */ + fun chownSh(dir: String?, groupAndUser: String?): Boolean { try { - PackageManager pm = context.getPackageManager(); - ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); - return appInfo.sourceDir; // 返回 APK 的完整路径 - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return ""; + val cmd = "chown -R $groupAndUser $dir" + Log.i("ShellUtils", "chownSh cmd:$cmd") + MockTools.exec(cmd) + return true + } catch (e: Exception) { + e.printStackTrace() + } + return false + } + + fun getPackagePath(context: Context, packageName: String): String { + return try { + val pm = context.packageManager + val appInfo = pm.getApplicationInfo(packageName, 0) + appInfo.sourceDir // 返回 APK 的完整路径 + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + "" } } - public static void exec(String cmd) { + fun copyFolderSh(oldPath: String, newPath: String): Boolean { + Log.i("ShellUtils", "start copyFolderSh : $oldPath ; $newPath") + try { - LogUtils.e(Log.INFO, "ShellUtils", "Executing command: " + cmd, null); - Process process = Runtime.getRuntime().exec(cmd); - process.waitFor(); - } catch (Exception e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: " + e.getMessage(), e); + val cmd = "cp -r -f $oldPath $newPath" + Log.i("ShellUtils", "copyFolderSh cmd:$cmd") + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + + return false + } + + /** + * 执行shell命令以列出目录的内容,重定向输出 + * 到指定的文件,并将文件所有权修改给特定用户。 + * + * @param上下文用于检索文件路径和用户信息的应用程序上下文。 + * @param dir需要列出其内容的目录。 + * @return `true`如果操作成功而没有异常,`false`否则。 + */ + fun listSh(context: Context, dir: String): Boolean { + Log.i("ShellUtils", "listSh->dir:$dir") + try { + val txtFileName = getRecordListTxtFileName(context) + val cmd = "ls $dir -l > $txtFileName" + MockTools.exec(cmd) + val uid = getPackageUserID( + context, + recordPackageName ?: "" + ) + chownSh(getMonitorDir(context), uid) + return true + } catch (e: Exception) { + e.printStackTrace() + } + return false + } + + + fun copyFileSh(oldPath: String, newPath: String): Boolean { + Log.i("ShellUtils", "start copyFileSh : $oldPath ; $newPath") + try { + val cmd = "cp -r -f $oldPath $newPath" + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + + return false + } + + + fun exec(cmd: String) { + try { + LogUtils.e(Log.INFO, "ShellUtils", "Executing command: $cmd", null) + val process = Runtime.getRuntime().exec(cmd) + process.waitFor() + } catch (e: Exception) { + LogUtils.e(Log.ERROR, "ShellUtils", "Error executing command: ${e.message}", e) } } - public static int getPid(Process p) { - int pid = -1; - try { - Field f = p.getClass().getDeclaredField("pid"); - f.setAccessible(true); - pid = f.getInt(p); - f.setAccessible(false); - } catch (Throwable e) { - pid = -1; + fun getPid(p: Process): Int { + var pid = -1 + return try { + val f = p.javaClass.getDeclaredField("pid") + f.isAccessible = true + pid = f.getInt(p) + f.isAccessible = false + pid + } catch (e: Throwable) { + pid } - return pid; } - public static boolean hasBin(String binName) { - // 验证 binName 是否符合规则 - if (binName == null || binName.isEmpty()) { - LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name",null); - throw new IllegalArgumentException("Bin name cannot be null or empty"); + fun hasBin(binName: String): Boolean { + if (binName.isEmpty()) { + LogUtils.e(Log.ERROR, "ShellUtils", "Invalid bin name", null) + throw IllegalArgumentException("Bin name cannot be null or empty") } - for (char c : binName.toCharArray()) { + for (c in binName.toCharArray()) { if (!Character.isLetterOrDigit(c) && c != '.' && c != '_' && c != '-') { - throw new IllegalArgumentException("Invalid bin name"); + throw IllegalArgumentException("Invalid bin name") } } - // 获取 PATH 环境变量 - String pathEnv = System.getenv("PATH"); - if (pathEnv == null) { - LogUtils.e(Log.ERROR, "ShellUtils", "PATH environment variable is not available", null); - return false; - } - - // 使用适合当前系统的路径分隔符分割路径 - String[] paths = pathEnv.split(File.pathSeparator); - for (String path : paths) { - File file = new File(path, binName); // 使用 File 构造完整路径 + val pathEnv = System.getenv("PATH") ?: return false + val paths = pathEnv.split(File.pathSeparatorChar) + for (path in paths) { + val file = File(path, binName) try { - // 检查文件是否可执行 if (file.canExecute()) { - return true; + return true } - } catch (SecurityException e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Security exception occurred while checking: " + file.getAbsolutePath(), e); + } catch (e: SecurityException) { + LogUtils.e( + Log.ERROR, + "ShellUtils", + "Security exception occurred while checking: ${file.absolutePath}", + e + ) } } - - // 如果未找到可执行文件,返回 false - return false; + return false } - public static String execRootCmdAndGetResult(String cmd) { - Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: " + cmd); -// if (!isCommandSafe(cmd)) { // 检查命令的合法性 -// Log.e("ShellUtils", "Detected unsafe command. Aborting execution."); -// throw new IllegalArgumentException("Detected unsafe command."); -// } + fun execRootCmdAndGetResult(cmd: String): String { + Log.d("ShellUtils", "execRootCmdAndGetResult - Started execution for command: $cmd") - Process process = null; - ExecutorService executor = Executors.newFixedThreadPool(2); + val process: Process? = when { + hasBin("su") -> Runtime.getRuntime().exec("su") + hasBin("xu") -> Runtime.getRuntime().exec("xu") + hasBin("vu") -> Runtime.getRuntime().exec("vu") + else -> Runtime.getRuntime().exec("sh") + } - try { - if (hasBin("su")) { - process = Runtime.getRuntime().exec("su"); - } else if (hasBin("xu")) { - process = Runtime.getRuntime().exec("xu"); - } else if (hasBin("vu")) { - process = Runtime.getRuntime().exec("vu"); - } else { - process = Runtime.getRuntime().exec("sh"); - } + val executor = Executors.newFixedThreadPool(2) + return try { + process?.let { + val os = BufferedOutputStream(it.outputStream) + val isInputStream = it.inputStream + val errorStream = it.errorStream + val br = + BufferedReader(InputStreamReader(isInputStream, StandardCharsets.UTF_8)) + val errorReader = + BufferedReader(InputStreamReader(errorStream, StandardCharsets.UTF_8)) - try (OutputStream os = new BufferedOutputStream(process.getOutputStream()); - InputStream is = process.getInputStream(); - InputStream errorStream = process.getErrorStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); - BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) { - - executor.submit(() -> { - String line; + executor.submit { try { - while ((line = errorReader.readLine()) != null) { - LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: " + line, null); + var line: String? + while (true) { + line = errorReader.readLine() + if (line == null) { + break + } + LogUtils.e(Log.ERROR, "ShellUtils", "Shell Error: $line", null) } - } catch (IOException e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error while reading process error stream: " + e.getMessage(), e); + } catch (e: IOException) { + LogUtils.e( + Log.ERROR, + "ShellUtils", + "Error while reading process error stream: ${e.message}", + e + ) } - }); - - os.write((cmd + "\n").getBytes()); - os.write("exit\n".getBytes()); - os.flush(); - StringBuilder output = new StringBuilder(); - String line; - while ((line = br.readLine()) != null) { - output.append(line).append("\n"); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (!process.waitFor(10, TimeUnit.SECONDS)) { - process.destroyForcibly(); - throw new RuntimeException("Shell command execution timeout."); + os.write("$cmd\n".toByteArray()) + os.write("exit\n".toByteArray()) + os.flush() + + val output = StringBuilder() + var line: String? + while (true) { + line = br.readLine() + if (line == null) { + break } + output.append(line).append("\n") + } + + + val completed = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + it.waitFor(10, TimeUnit.SECONDS) } else { - long startTime = System.currentTimeMillis(); + val startTime = System.currentTimeMillis() while (true) { try { - process.exitValue(); - break; - } catch (IllegalThreadStateException e) { - if (System.currentTimeMillis() - startTime > 10000) { // 10 seconds - process.destroy(); - throw new RuntimeException("Shell command execution timeout."); + it.exitValue() + break + } catch (e: IllegalThreadStateException) { + if (System.currentTimeMillis() - startTime > 10000) { + it.destroy() + throw RuntimeException("Shell command execution timeout.") } - Thread.sleep(100); // Sleep briefly before re-checking + Thread.sleep(100) } } + true } - return output.toString().trim(); - } - } catch (IOException | InterruptedException e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: " + e.getMessage(), e); - Thread.currentThread().interrupt(); - return "Error: " + e.getMessage(); + + output.toString().trim() + } ?: "" + } catch (e: IOException) { + LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e) + Thread.currentThread().interrupt() + "Error: ${e.message}" + } catch (e: InterruptedException) { + LogUtils.e(Log.ERROR, "ShellUtils", "Command execution failed: ${e.message}", e) + Thread.currentThread().interrupt() + "Error: ${e.message}" } finally { - if (process != null) { - process.destroy(); - } - executor.shutdown(); - Log.d("ShellUtils", "Executor service shut down."); + process?.destroy() + executor.shutdown() + Log.d("ShellUtils", "Executor service shut down.") } } - public static void execRootCmd(String cmd) { - // 校验命令是否安全 -// if (!isCommandSafe(cmd)) { -// return; -// } - List cmds = new ArrayList<>(); - cmds.add(cmd); - - // 使用同步锁保护线程安全 - synchronized (ShellUtils.class) { + fun execRootCmd(cmd: String) { + val cmds = mutableListOf(cmd) + synchronized(ShellUtils::class.java) { try { - List 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 execRootCmds(List cmds) { - List results = new ArrayList<>(); - Process process = null; + fun execRootCmds(cmds: List): List { + val results = mutableListOf() + var process: Process? = null try { - // 初始化 Shell 环境 - process = hasBin("su") ? Runtime.getRuntime().exec("su") : Runtime.getRuntime().exec("sh"); - - // 启动读取线程 - Process stdProcess = process; - Thread stdThread = new Thread(() -> { - try (BufferedReader stdReader = new BufferedReader(new InputStreamReader(stdProcess.getInputStream()))) { - List localResults = new ArrayList<>(); - String line; - while ((line = stdReader.readLine()) != null) { - localResults.add(line); - Log.d("ShellUtils", "Stdout: " + line); - } - synchronized (results) { - results.addAll(localResults); - } - } catch (IOException ioException) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout", ioException); - } - }); - - Process finalProcess = process; - Thread errThread = new Thread(() -> { - try (BufferedReader errReader = new BufferedReader(new InputStreamReader(finalProcess.getErrorStream()))) { - String line; - while ((line = errReader.readLine()) != null) { - LogUtils.e(Log.ERROR, "ShellUtils", "Stderr: " + line, null); - } - } catch (IOException ioException) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stderr", ioException); - } - }); - - // 启动子线程 - stdThread.start(); - errThread.start(); - - try (OutputStream os = process.getOutputStream()) { - for (String cmd : cmds) { -// if (!isCommandSafe(cmd)) { -// Log.w("ShellUtils", "Skipping unsafe command: " + cmd); -// continue; -// } - os.write((cmd + "\n").getBytes()); - Log.d("ShellUtils", "Executing command: " + cmd); - } - os.write("exit\n".getBytes()); - os.flush(); + process = if (hasBin("su")) { + Runtime.getRuntime().exec("su") + } else { + Runtime.getRuntime().exec("sh") } + val stdProcess = process + val stdThread = Thread { + try { + val stdReader = BufferedReader(InputStreamReader(stdProcess.inputStream)) + val localResults = mutableListOf() + var line: String? + while (true) { + line = stdReader.readLine() + if (line == null) { + break + } + localResults.add(line) + Log.d("ShellUtils", "Stdout: $line") + } + synchronized(results) { + results.addAll(localResults) + } + } catch (ioException: IOException) { + LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout", ioException) + } + } + + val errThread = Thread { + try { + val errReader = BufferedReader(InputStreamReader(process.errorStream)) + var line: String? + while (true) { + line = errReader.readLine() + if (line == null) { + break + } + LogUtils.e(Log.ERROR, "ShellUtils", "Stderr: $line", null) + } + } catch (ioException: IOException) { + LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stderr", ioException) + } + } + + stdThread.start() + errThread.start() + + val os = process.outputStream + for (cmd in cmds) { + os.write("$cmd\n".toByteArray()) + Log.d("ShellUtils", "Executing command: $cmd") + } + os.write("exit\n".toByteArray()) + os.flush() + try { - // 执行命令、等待解决 - process.waitFor(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // 恢复中断 - LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e); + process.waitFor() + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e) } - // 等待子线程完成 - stdThread.join(); - errThread.join(); + stdThread.join() + errThread.join() - } catch (InterruptedIOException e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e); - Thread.currentThread().interrupt(); // 恢复线程的中断状态 - } catch (Exception e) { - LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e); + } catch (e: InterruptedIOException) { + LogUtils.e(Log.ERROR, "ShellUtils", "Error reading stdout: Interrupted", e) + Thread.currentThread().interrupt() + } catch (e: Exception) { + LogUtils.e(Log.ERROR, "ShellUtils", "Error executing commands", e) } finally { - if (process != null) { - process.destroy(); - } + process?.destroy() } - return results; + return results } - public static boolean hasRootAccess() { - // 记录是否出现安全异常 - boolean hasSecurityError = false; + /** + * 使用Shell命令删除给定文件名指定的文件或目录。 + * 此方法执行“ RM -RF”命令以删除文件或目录。 + * 记录过程步骤并处理执行过程中可能发生的任何异常。 + * + * @param文件名删除文件或目录的名称或路径 + */ + fun delFileSh(fileName: String) { + Log.i("ShellUtils", "start delFileSh : $fileName") + try { + val cmd = "rm -rf $fileName" + Log.i("ShellUtils", "delFileSh-> cmd:$cmd") + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + } - // 检查二进制文件 - String[] binariesToCheck = {"su", "xu", "vu"}; - for (String bin : binariesToCheck) { + fun delFolderSh(dir: String, fileName: String) { + Log.i("ShellUtils", "start delFolderSh :$fileName") + var PrintWriter: PrintWriter? = null + var process: Process? = null + try { + process = Runtime.getRuntime().exec("su") + PrintWriter = PrintWriter(process.outputStream) + + var cmd = "cd $dir \n" + cmd += "rm -rf $fileName" + Log.i("ShellUtils", "delFolderSh-> cmd:$cmd") + PrintWriter.println(cmd) + + PrintWriter.flush() + PrintWriter.close() + val value = process.waitFor() + } catch (e: Exception) { + e.printStackTrace() + } finally { + process?.destroy() + } + } + + fun delFile(file: File) { + try { + val cmd = "rm -rf $file" + Log.i("ShellUtils", "delFile-> cmd:$cmd") + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun give777(path: String): Boolean { + execCommand("su") + execCommand("chmod", "777", path) + return true + } + + fun delFilesSh(dir: String, prefix: String?) { + try { + var cmd = "cd $dir|" + cmd += "rm -rf $prefix*" + Log.i("ShellUtils", "delFilesSh-> cmd:$cmd") + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun hasRootAccess(): Boolean { + var hasSecurityError = false + val binariesToCheck = arrayOf("su", "xu", "vu") + for (bin in binariesToCheck) { try { if (hasBin(bin)) { - return true; + return true } - } catch (SecurityException e) { - hasSecurityError = true; - LogUtils.e(Log.ERROR, "ShellUtils", "Security exception while checking: " + bin, e); + } catch (e: SecurityException) { + hasSecurityError = true + LogUtils.e( + Log.ERROR, + "ShellUtils", + "Security exception while checking: $bin", + e + ) } } - // 判断如果发生安全异常则反馈问题 if (hasSecurityError) { - Log.w("ShellUtils", "Potential security error detected while checking root access."); + Log.w("ShellUtils", "Potential security error detected while checking root access.") + } + return false + } + + fun unZipFileSh(zipFileName: String, dataDir: String) { + try { + var cmd = "cd $dataDir|" + cmd += "tar -xvf " + File(zipFileName).name + Log.i("ShellUtils", "unZipFileSh-> cmd:$cmd") + MockTools.exec(cmd) + } catch (e: Exception) { + e.printStackTrace() + } + } + + /* + * m命令可以通过adb在shell中执行,同样,我们可以通过代码来执行 + */ + fun execCommand(vararg command: String?): String? { + var process: Process? = null + var errIs: InputStream? = null + var inIs: InputStream? = null + var result: String? = "" + + try { + process = ProcessBuilder().command(*command).start() + + val baos = ByteArrayOutputStream() + var read = -1 + errIs = process.errorStream + while ((errIs.read().also { read = it }) != -1) { + baos.write(read) + } + + inIs = process.inputStream + while ((inIs.read().also { read = it }) != -1) { + baos.write(read) + } + result = String(baos.toByteArray()) + + inIs?.close() + errIs?.close() + process.destroy() + } catch (e: IOException) { + result = e.message } - // 没有找到合法的二进制文件,则认为无root权限 - return false; + return result + } + + fun unzipAPkSh(zipFileName: String, dataDir: String) { + try { + val file = File(dataDir) + if (!file.exists()) { + FileUtils.forceCreteDir(file) + } + val cmd = "unzip -o " + "/sdcard/apks/" + File(zipFileName).name + " -d " + dataDir + val unzipResult = MockTools.execRead(cmd) + Log.i("ShellUtils", "unZipFileSh-> cmd:$unzipResult") + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * 执行shell命令以检索指定文件的内容。 + * + * @param上下文应用程序上下文 + * @param fileName要读取的文件的名称 + * @return文件的内容作为字符串或一个空字符串,如果发生异常 + */ + fun catSh(context: Context?, fileName: String): String { + Log.i("TaskUtils", "catSh->fileName:$fileName") + try { + val cmd = "cat $fileName" + val result = MockTools.execRead(cmd) + return result + } catch (e: Exception) { + e.printStackTrace() + } + return "" } } diff --git a/app/src/main/java/com/android/grape/util/TaskUtils.kt b/app/src/main/java/com/android/grape/util/TaskUtils.kt new file mode 100644 index 0000000..a86d917 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/TaskUtils.kt @@ -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.. 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() + } +} diff --git a/app/src/main/java/com/android/grape/util/Util.kt b/app/src/main/java/com/android/grape/util/Util.kt deleted file mode 100644 index 8ef66e4..0000000 --- a/app/src/main/java/com/android/grape/util/Util.kt +++ /dev/null @@ -1,3132 +0,0 @@ -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.pm.ApplicationInfo -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.content.res.AssetManager -import android.net.Uri -import android.os.Binder -import android.os.Build -import android.os.Environment -import android.os.Handler -import android.os.Looper -import android.provider.Settings -import android.util.Log -import com.android.grape.MainApplication -import com.android.grape.job.AutoJobService -import com.android.grape.job.DownloadAppJobService -import com.android.grape.job.MonitorService -import com.android.grape.job.UnInstallService -import com.android.grape.net.HttpUtils -import com.android.grape.net.MyPost -import com.android.grape.sai.ApkSourceBuilder -import com.android.grape.sai.FlexSaiPackageInstaller -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.blankj.utilcode.util.ActivityUtils -import com.blankj.utilcode.util.LogUtils -import org.json.JSONObject -import java.io.BufferedReader -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.FileWriter -import java.io.IOException -import java.io.InputStream -import java.io.InputStreamReader -import java.io.LineNumberReader -import java.io.PrintWriter -import java.io.UnsupportedEncodingException -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLDecoder -import java.util.Locale -import java.util.Properties -import java.util.Random -import kotlin.math.min - -/** - * `util`类用作封装多种方法和属性的实用程序类 - * 用于管理与应用程序相关的数据并执行各种操作。此课程提供 - * 诸如检索和更新应用程序元数据,管理日志,处理等功能 - * 备份要求,跟踪用户和组信息以及与shell命令进行交互。 - * - * 它还揭示了使用JSON数据,时间戳,代理配置,软件包的方法 - * 所有权和其他特定于应用程序的属性。该课程中的许多方法都相关 - * 将应用程序状态管理和设备上的特定命令执行。 - * - * 注意:此类包括用于初始化和内部配置的方法 - * 不直接暴露于外部使用。 - */ -object Util { - private var nRandom = 0 - private const val TAG = "IOSTQ:Util" - private const val tag = "3" - private const val sendRefer = true - var isNeedRestored: Boolean = false - 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" - private const val hookPackageName = "com.affsystem.androidhooker" - private const val hookAppMainClass = "com.affsystem.androidhooker.MainActivity" - private const val monitorDir = "monitor" - private const val apkDir = "apks" - private const val monitorFile = "monitor.txt" - private const val taskFile = "task.txt" - private var defaultAppJo = JSONObject() - private var defaultPRoxyJo = JSONObject() - val country: String? = null - var reloginRecordId: Long = 0L - private var appVer: String? = null - private var appVerCode: Long? = null - - /** - * 任务json - */ - var taskJson: JSONObject? = null - private set - var paramsJson: JSONObject? = null - private set - private val cacheJson: JSONObject? = null - var recordPackageName: String? = null - var recordFileName: String? = null - private var recordExtraFileName: 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 - private var startInstallTime = 0L - var videoProxy: String = "" - var forwardIp: String? = "" - private var referer: String? = null - private var appDataUrl = "" - private var afLog = "" - var afApp: String = "" - var preClickRecordId: Long = 0 - var backFileName: String? = null - private set - var backFileName1: String? = null - private set - var backFileName2: String? = null - private set - - private var installRet: Boolean? = null - var isClickRet: Boolean = false - var clickErrReason: String = "" - private var appAfVer = "" - 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 = "" - 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 backupResult: JSONObject? = null - var backUpServerIp: String = "" - var isCanceled: Boolean = false - var isPaused: Boolean = false - var ctit: Int = 0 - private set - var keepOpen: Int = 0 - private var tags: String? = null - private const val isCollectURL = false - var fuzzy_domain: String = "" - var fuzzy_proxy: String? = "" - var script_status: Int = 0 - private const val testForSetInfo = false - var logBuffer: StringBuffer = StringBuffer() - private set - var scriptOpenApp: Int = 0 - private var mainUserAndGroup: String? = null - - private const val apk_path = "/sdcard/Android/data/sperixlabs.proxyme/files/monitor/apks" - - var script_path: String = "/sdcard/apks/script.zip" - - private var name: String? = null - private var baoming: String? = null - private var zip_name: String? = null - private var oldpath: String? = null - private var newpath: String? = null - - init { - initDefaultAppJo() - initDefaultProxyJo() - } - - /** 检索与应用程序关联的主要用户和组信息。 - * 如果该信息尚未缓存,则将计算并存储以供将来访问。 - * - * @param上下文用于检索软件包信息和文件路径的应用程序上下文。 - * @return代表与应用程序关联的主要用户和组的字符串。 - */ - fun getMainUserAndGroup(context: Context): String? { - if (mainUserAndGroup == null) { - mainUserAndGroup = getUserAndGroupSh( - context, - context.packageName, - getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt" - ) - } - - Log.i(TAG, "mainUserAndGroup:" + mainUserAndGroup) - - return mainUserAndGroup - } - - 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" - } - - fun getAppAfVer(): String { - return appAfVer - } - - fun setAppAfVer(appAfVerV: String) { - Log.i(TAG, "setAppAfVer: $appAfVerV") - appAfVer = appAfVerV - } - - var appVersion: String? - get() = appVer - set(appAfVerV) { - Log.i(TAG, "setAppAfV: $appAfVerV") - appVer = appAfVerV - } - - var appVersionCode: Long? - get() = appVerCode - set(appVerCode) { - Log.i(TAG, "setAppAfVerCode: $appVerCode") - Util.appVerCode = appVerCode - } - - - fun setInstallRet(installRetV: Boolean) { - Log.i(TAG, "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(TAG, "setReferer: $refererV") - referer = refererV - } - - fun getReferer(): String? { - return referer - } - - fun setRecordExtraFileName(extraFileNameV: String?) { - recordExtraFileName = extraFileNameV - } - - fun HkLog(): String { - return "" - } - - fun HkVer(): String { - return "v1060" - } - - private 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() - } - - private 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() - } - } - - private fun initDefaultProxyJo() { - try { - defaultPRoxyJo = JSONObject("{\"proxyPort\": 0,\"proxyIp\": \"\"}") - } catch (e: Exception) { - } - } - - /** - * 递归更改指定目录及其内容的所有权 - * 使用Shell命令给提供的用户和组。 - * - * @param上下文调用该方法的上下文 - * @param dir是将更改所有权的目录的路径 - * @param groupanduser组和用户以“组:用户”的形式 - * @return true如果操作成功,则否则为否则 - */ - fun chownSh(dir: String?, groupAndUser: String?): Boolean { - try { - val cmd = "chown -R $groupAndUser $dir" - Log.i(TAG, "chownSh cmd:$cmd") - MockTools.exec(cmd) - return true - } catch (e: Exception) { - e.printStackTrace() - } - return false - } - - /** - * 检索设备上与特定软件包名称关联的用户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 - } - - - /** - * 检查应用程序是否需要通过比较当前安装的版本进行升级 - * 带有录制版本。 - * - * @param上下文应用程序访问软件包管理器以获取的上下文 - * 包装信息。 - * @return a布尔值,指示是否需要更新。如果版本代码返回true - * 不匹配或找不到软件包,否则为错误。 - */ - //判断版本是否更新 - fun CheckAppNeedUpgrade(context: Context): Boolean { - val pckMan: PackageManager = context.packageManager - val items = ArrayList>() - val packageInfo: List = pckMan.getInstalledPackages(0) - - for (pInfo in packageInfo) { - if (pInfo.packageName == recordPackageName) { - return if (appVersionCode != pInfo.versionCode.toLong()) { - true - } else { - false - } - } - } - return true - } - - /** - * 执行shell命令以列出目录的内容,重定向输出 - * 到指定的文件,并将文件所有权修改给特定用户。 - * - * @param上下文用于检索文件路径和用户信息的应用程序上下文。 - * @param dir需要列出其内容的目录。 - * @return `true`如果操作成功而没有异常,`false`否则。 - */ - fun listSh(context: Context, dir: String): Boolean { - Log.i(TAG, "listSh->dir:$dir") - try { - val txtFileName = getRecordListTxtFileName(context) - val cmd = "ls $dir -l > $txtFileName" - MockTools.exec(cmd) - val uid = getPackageUserID( - context, - recordPackageName ?: "" - ) - chownSh(getMonitorDir(context), uid) - return true - } catch (e: Exception) { - e.printStackTrace() - } - return false - } - - - fun getCatFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/catSh.txt" - } - - fun getMonitorDir(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" - } - - - /** - * 执行shell命令以检索指定文件的内容。 - * - * @param上下文应用程序上下文 - * @param fileName要读取的文件的名称 - * @return文件的内容作为字符串或一个空字符串,如果发生异常 - */ - fun catSh(context: Context?, fileName: String): String { - Log.i(TAG, "catSh->fileName:$fileName") - try { - val cmd = "cat $fileName" - val result = MockTools.execRead(cmd) - return result - } catch (e: Exception) { - e.printStackTrace() - } - return "" - } - - /** - * 获得某个安装包的安装用户 - * - * @param context - * @return - */ - fun getUserAndGroupSh(context: Context): String { - return getUserAndGroupSh( - context, - recordPackageName, getRecordTxtFileName(context) - ) - } - - fun getSelfUserAndGroupSh(context: Context): String { - return getSelfUserAndGroupSh( - context, - context.packageName, - getSelfRecordTxtFileName(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(TAG, "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( - 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() - } finally { - process?.destroy() - } - - return "" - } - - /** - * 检索指定软件包的用户和组信息。 - * - * 此方法以特定方式执行命令列出目录内容并解析结果 - * 提取与软件包名称关联的用户和组信息。 - * - * @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 "" - } - - - /** - 构建请求参数:获取设备唯一 ID,拼接请求 URL。 - 发送 POST 请求:调用 MyPost.postData 向服务器提交请求。 - 处理响应结果: - 成功时设置权限、解析 JSON 数据并设置 clickTime。 - 失败或异常时调用 setFinish 结束任务。 - */ - fun execReloginTask(context: Context) { - init() - // adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context); - val url = "http://39.103.73.250/tt/ddj/preRequest!requestRelogin.do" - // String params = "platform=Android&tag="+getTag(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? = MyPost.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") - execSetJson(context) - clickTime = 1 - } else { - Log.i( - TAG, - "request result code invalid : $code" - ) - setFinish(context) - } - } - } else { - Log.i(TAG, "request result is null") - setFinish(context) - } - } catch (e: Exception) { - Log.i(TAG, "execTask error:" + e.message) - setFinish(context) - } - } - - /** - * 初始化并构建请求参数:获取设备唯一 ID(ANDROID_ID),拼接请求 URL。 - * 发送 POST 请求:通过 MyPost.postData 向服务器提交安装请求。 - * 处理响应结果: - * 成功时解析 JSON 数据、设置权限,并触发 clickTime 为 1 表示需执行点击操作。 - * 失败或异常时调用 setFinish 结束任务。 - */ - fun execInstallTask(context: Context) { - 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? = MyPost.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(TAG, "request result code invalid : $code"); - setFinish(context) - } - } else { - LogUtils.i(TAG, "request result is null"); - setFinish(context) - } - } catch (e: Exception) { - LogUtils.i(TAG, "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 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() - } - - fun printStr(ss: String?) { - if (ss != null && ss.length > 0) { - val length = ss.length - - val loop = (length - 1) / 2000 + 1 - - for (i in 0.. 0 - - if (it.has("ExecScriptZipURL") && !it.isNull("ExecScriptZipURL")) { - canAutoLc = it.getString("ExecScriptZipURL") - } - - } - } - } 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() - } - } - - - /** - * 通过向指定的服务器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(TAG, "url:$url") - - try { - val ret: String? = MyPost.postData("".toByteArray(), url) - Log.i(TAG, "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 对象添加各种属性来初始化操作所需的 JSON 参数。 - * 处理设备、优惠和其他相关数据,以准备完整的 JSON 结构。 - * - * @param context 用于执行各种操作的应用程序上下文,例如在初始化期间写入文件和发送事件。 - * 例如,在初始化期间写入文件和发送事件。 - * @return 如果 JSON 参数初始化成功,则返回 true,否则返回 false - */ - private 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(TAG, "initParamsJson error : " + e.message) - setFinish(context) - return false - } - } - - return true - } - - private fun execRecord(context: Context) { - Log.i(TAG, "start to execRecord") - val apks = File(getRecordDataDirName(context)) - if (!apks.exists()) { - forceMakeDir(apks) - } - DownloadAppJobService.onEvent(context) - } - - - private fun execDownScript(): Boolean { - var isDownload = true - if (isCanAuto) { - Log.i(TAG, "start to execDownScript") - val script_url = "http://39.103.73.250/tt/" + canAutoLc - isDownload = downloadFile(script_url, script_path) - if (!isDownload) { - Log.i(TAG, "execDownScript isDownload : $isDownload") - return false - } - unzipAPkSh(script_path, "/sdcard/script/") - delFileSh(script_path) - } - return isDownload - } - - - fun execDownload(context: Context): Boolean { - return execDownloadApp(context) && execDownScript() - } - - 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(TAG, "download apk succ") - Log.i(TAG, "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 - } - - private fun getRecordExtraFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordExtraFileName - } - - fun execDownloadData(context: Context): Boolean { - try { - return downloadFile(appDataUrl, getRecordDataFileName(context)) - } catch (e: Exception) { - e.printStackTrace() - } - - return false - } - - - 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) - } - - - @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("文件写入", "成功") - } - - - @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 = AdvertisingIdClient.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 - } - - @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) - - Util.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 = AdvertisingIdClient.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(TAG, "set info ret : " + ret); - } catch (e: Exception) { - e.printStackTrace() - } - return true - } - - val paramString: String - get() = paramsJson.toString() - - private fun getRecordTxtFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".txt" - } - - private fun getSelfRecordTxtFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + context.packageName + ".txt" - } - - private fun getRecordListTxtFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".list.txt" - } - - private fun getRecordApkFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".apk" - } - - private fun getRecordApkVerFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordFileName - } - - private fun getRecordSdcardApkVerFileName(context: Context): String { - return "/sdcard/apks/" + recordFileName - } - - private fun getRecordDataFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" + recordPackageName + ".zip" - } - - private fun getRecordDataDirName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/" - } - - fun getBaseFilesDir(context: Context): String { - return context.filesDir.absolutePath - } - - private fun notcl(context: Context) { - val url = "http://127.0.0.1:8090/ctl/notcl" - - try { - val ret: String = MyPost.postData("".toByteArray(charset("utf-8")), url) ?: "" - Log.i(TAG, "notcl ret : $ret") - } catch (e: Exception) { - e.printStackTrace() - } - } - - private fun checkHook(context: Context): Boolean { - val url = "http://127.0.0.1:8090/ctl/test" - - val checked = false - - try { - val ret: String = MyPost.postData("".toByteArray(charset("utf-8")), url) ?: "" - Log.i(TAG, "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 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) - } - } - - - fun execTargetApp(context: Context, targetPackageName: String): Boolean { - Log.i(TAG, "开始启动应用 ...$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 - } - } - - - 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 execTargetApp( - context: Context, - targetPackageName: String, - targetAppMainClass: String - ): Boolean { - Log.i( - TAG, - "start to openTargetApp ... $targetPackageName ; $targetAppMainClass" - ) - - execTargetApp(context, targetPackageName) - return true - } - - private 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() - } - } - } - - private 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 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 getFileModifyTime(context: Context, dirName: String, fileName: String): Long { - val fs = File(getBaseFilesDir(context) + "/" + dirName + "/" + fileName) - - return if (fs.exists()) { - fs.lastModified() - } else { - 0L - } - } - - 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 "" - } - - - fun downloadFile(httpUrl: String, fileName: String): Boolean { - Log.i( - TAG, - "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()) { - 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(TAG, "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 - } - - /** - * 安装次留软件 - * - * @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(getRecordExtraFileName(context)) - } - - installRet = installApk(context, file, extraFile) && 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(TAG, "installRet:$installRet") - } else { - installRet = true - MockTools.exec("pm clear $recordPackageName") - } - - - return installRet - } - - fun isBackground(context: Context): Boolean { - val activityManager: ActivityManager = - context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - val appProcesses: List = - 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 - } - - private 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 - } - - fun recoverRecordData(context: Context): Boolean { - Log.i(TAG, "start recoverRecordData") - - try { - val dataDir = getRecordDataDirName(context) - val zipFile = getRecordDataFileName(context) - val reloginDataDir = getApkDataDir( - context, - recordPackageName ?: return false - ) - - Log.i( - TAG, - "recoverRecordData: dir=$reloginDataDir" - ) - if (reloginDataDir == null) { - return false - } - - unZipFileSh(zipFile, dataDir) - - val userAnGroup = getUserAndGroupSh(context) - Log.i(TAG, "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 - } - - fun delFilesSh(dir: String, prefix: String?) { - try { - var cmd = "cd $dir|" - cmd += "rm -rf $prefix*" - Log.i(TAG, "delFilesSh-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - fun unZipFileSh(zipFileName: String, dataDir: String) { - try { - var cmd = "cd $dataDir|" - cmd += "tar -xvf " + File(zipFileName).name - Log.i(TAG, "unZipFileSh-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - fun unzipAPkSh(zipFileName: String, dataDir: String) { - try { - val file = File(dataDir) - if (!file.exists()) { - forceCreteDir(file) - } - val cmd = "unzip -o " + "/sdcard/apks/" + File(zipFileName).name + " -d " + dataDir - val unzipResult = MockTools.execRead(cmd) - Log.i(TAG, "unZipFileSh-> cmd:$unzipResult") - } catch (e: Exception) { - e.printStackTrace() - } - } - - fun copyFolderSh(oldPath: String, newPath: String): Boolean { - Log.i(TAG, "start copyFolderSh : $oldPath ; $newPath") - - try { - val cmd = "cp -r -f $oldPath $newPath" - Log.i(TAG, "copyFolderSh cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - - return false - } - - fun copyFileSh(oldPath: String, newPath: String): Boolean { - Log.i(TAG, "start copyFileSh : $oldPath ; $newPath") - try { - val cmd = "cp -r -f $oldPath $newPath" - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - - return false - } - - fun installApk(context: Context, apkFile: File, extraFile: File?): Boolean { - Log.i( - TAG, - "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) - } - } - } - - - fun installApks4Tmp(apkName: String?, context: Context): Boolean { - var result = MockTools.execRead("pm install-create") - Log.d(TAG, "installApks4Tmp: successMsg $result") - val session = result.substring(result.indexOf("[") + 1, result.indexOf("]")) - Log.d(TAG, "installApks4Tmp: session $session") - - val file = File(getRecordDataDirName(context)) - Log.d(TAG, "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( - TAG, - "installApks4Tmp: extraName $extraName" - ) - if ("apk" == extraName) { - //Log.d(TAG, "installApks4Tmp: getPath " + f.getPath()); - val commond = - "pm install-write " + session + " " + currentApkFile + ".apk " + f.path - currentApkFile++ - //Log.d(TAG, "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 clientInstallOther(context: Context, apkFile: File): Boolean { - Log.e(TAG, "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() - } - - private 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(TAG, "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 - } - - private fun getSessionTxtFileName(context: Context): String { - return getBaseFilesDir(context) + "/" + monitorDir + "/" + apkDir + "/sessionTxt.txt" - } - - private fun clientInstallObb(context: Context, apkFile: File, extraFile: File): Boolean { - Log.i( - TAG, - "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 - } - - private fun installObb(context: Context, extraFile: File) { - val userAndGroup = getUserAndGroupSh(context) - - 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() - } - } - } - } - - /* - * 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 - } - - private fun give777(path: String): Boolean { - execCommand("su") - execCommand("chmod", "777", path) - return true - } - - 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 - } - - private fun clientInstall(apkFile: File): Boolean { - Log.i(TAG, "clientInstall : $apkFile") - try { - MockTools.exec("chmod 777 $apkFile") - MockTools.exec("pm install -r $apkFile") - return true - } catch (e: Exception) { - e.printStackTrace() - } - return false - } - - private fun returnResult(value: Int): Boolean { - // 代表成功 - return if (value == 0) { - true - } else if (value == 1) { // 失败 - false - } else { // 未知情况 - false - } - } - - fun hookOpenApp(context: Context) { - Log.i(TAG, "start to hookOpenApp : ") - - val url = "http://127.0.0.1:8090/ctl/setTarget?target=" + recordPackageName - - val ret: String = MyPost.postData("".toByteArray(), url) ?: "" - - Log.i(TAG, "Hook Open App ret : $ret") - } - - /** - * 卸载 - * - * @return - */ - fun clientUninstallRecord(context: Context): Boolean { - while (recordAppInstalled(context)) { - clientUninstall(recordPackageName) - } - return true - } - - /** - * 静默卸载 - */ - fun clientUninstall(packageName: String?): Boolean { - Log.i( - TAG, - "start to clientUninstall : $packageName" - ) - try { - val cmd = "pm uninstall $packageName" - MockTools.exec(cmd) - return true - } catch (e: Exception) { - e.printStackTrace() - } - return false - } - - private fun recordAppInstalled(context: Context): Boolean { - return checkAppInstalled( - context, - recordPackageName - ) - } - - private 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(TAG, "checkAppInstalled($pkgName) : $exists") - - return exists - } - - fun setFinish(context: Context) { - Log.i(TAG, "setFinish") - MonitorService.setRunning(false) - killRecordProcess(context, AUTO_JSPACKAGENAME) - Handler(Looper.getMainLooper()).postDelayed(Runnable { - MonitorService.onEvent( - context - ) - }, 5000) - } - - 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 ?: "" - } - - 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() - } - - private 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..= 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) - } - } - } - - //应用藏到后台 - fun setBackApp(context: Context) { - Log.i(TAG, "start to setBackApp") - if (isRunningForeground(context)) { - Handler(Looper.getMainLooper()).post(Runnable { - Log.i(TAG, "setBackApp exec") - ActivityUtils.getTopActivity()?.moveTaskToBack(true) - }) - } else { - Log.i(TAG, "app is back already") - } - } - - /** - * 集上应用 - * - * @param context 上下文 - */ - //当本应用位于后台时,则将它切换到最前端 - fun setTopApp(context: Context) { - Log.i(TAG, "start to setTopApp"); - if (isRunningForeground(context)) { - Log.i(TAG, "app isRunningForeground"); - return; - } - //获取ActivityManager - val activityManager: ActivityManager = - context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - - //获得当前运行的task(任务) - val taskInfoList: List = - activityManager.getRunningTasks(100) - for (taskInfo in taskInfoList) { - //找到本应用的 task,并将它切换到前台 - if (taskInfo.baseActivity?.packageName == context.packageName) { -// Log.i(TAG, "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.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 delFiles(context: Context?) { - Log.i(TAG, "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) - } - } - } - } - } - - - private 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(TAG, "makefile-> cmd:$cmd") - PrintWriter.println(cmd) - - PrintWriter.flush() - PrintWriter.close() - val value = process.waitFor() - } catch (e: Exception) { - e.printStackTrace() - } finally { - process?.destroy() - } - } - - private fun delFile(file: File) { - try { - val cmd = "rm -rf $file" - Log.i(TAG, "delFile-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - fun tagForCapture(context: Context): Boolean { - val tt = getTag(context) - return "101" == tt || "100" == tt - } - - fun delFolderSh(dir: String, fileName: String) { - Log.i(TAG, "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(TAG, "delFolderSh-> cmd:$cmd") - PrintWriter.println(cmd) - - PrintWriter.flush() - PrintWriter.close() - val value = process.waitFor() - } catch (e: Exception) { - e.printStackTrace() - } finally { - process?.destroy() - } - } - - /** - * 使用shell命令将目录压缩到zip文件中。 - * - * @param zipdirname要压缩目录的绝对路径 - * @param zipfileName所产生的zip文件的名称,包括其路径 - */ - fun zipSh(zipDirName: String, zipFileName: String) { - Log.i( - TAG, - "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(TAG, "zipSh-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - - /** - * 使用Shell命令删除给定文件名指定的文件或目录。 - * 此方法执行“ RM -RF”命令以删除文件或目录。 - * 记录过程步骤并处理执行过程中可能发生的任何异常。 - * - * @param文件名删除文件或目录的名称或路径 - */ - fun delFileSh(fileName: String) { - Log.i(TAG, "start delFileSh : $fileName") - try { - val cmd = "rm -rf $fileName" - Log.i(TAG, "delFileSh-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - /** - * 通过从应用程序数据文件夹创建压缩zip文件来备份指定软件包的数据文件。 - * 所得的zip文件存储在预定义的监视器目录中。 - * - * @param 上下文访问文件系统和应用程序信息所需的应用程序上下文。 - * @param packageName 将备份数据的软件包的名称。 - * @return 如果备份成功,则创建的zip文件的绝对路径,如果该过程失败,则为null。 - */ - fun backupDataFile(context: Context, packageName: String?): String? { - Log.i(TAG, "start backUpDataFile : $packageName") - if (packageName == null) { - return null - } - try { - val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName - val zipFileName = - getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + ".zip" - Log.i(TAG, "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(TAG, "backupDataFile->apkDataPath=$apkDataPath") - listSh(context, apkDataPath) - - val file = File(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(TAG, "line:$line") - val blankPos = line.lastIndexOf(" ") - val fn = line.substring(blankPos + 1) - copyFileSh("$apkDataPath/$fn", "$zipDirName/") - } - } - } - val uid = getUserAndGroupSh(context.applicationContext) - chownSh(getMonitorDir(context), uid) - val copySucc = File(zipDirName).exists() - Log.i(TAG, "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 - } - - fun backupDataFile1(context: Context, packageName: String): String? { - Log.i(TAG, "start backUpDataFile1 : $packageName") - try { - val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "1" - val zipFileName = - getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "1.zip" - Log.i( - TAG, - "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(TAG, "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 backupDataFile2(context: Context, packageName: String): String? { - Log.i(TAG, "start backUpDataFile2 : $packageName") - try { - val zipDirName = getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "2" - val zipFileName = - getBaseFilesDir(context) + "/" + monitorDir + "/" + packageName + "2.zip" - Log.i( - TAG, - "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( - TAG, - "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(TAG, "file need keep : " + f.absolutePath) - } - } else { - val fl = f.length() - if (fl > 1024 * 1024 * 3) { -// f.delete(); - delFile(f) - } else { - Log.i(TAG, "file need keep : " + f.absolutePath) - } - } - } - } - } - val copySucc = File(zipDirName).exists() - - Log.i( - TAG, - "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 - } - - /** - * 为指定上下文启动备份过程。 - * - * @param上下文用于启动备份过程的应用程序上下文 - */ - fun backUp(context: Context) { - killRecordProcess(context) - backUp(context, recordPackageName) - } - - - /** - * public static void killRecordProcess(Context context, String packageName) { - * Log.i(TAG, "start killRecordProcess :" + packageName); - * - * try { - * String cmd = "am force-stop " + packageName; - * Log.i(TAG, "killRecordProcess-> cmd:" + cmd); - * MockTools.exec(cmd); - * } catch (Exception e) { - * e.printStackTrace(); - * } - * } - */ - fun killRecordProcess(context: Context?, packageName: String?) { - Log.i(TAG, "start killRecordProcess :$packageName") - - try { - val cmd = "am force-stop $packageName" - Log.i(TAG, "killRecordProcess-> cmd:$cmd") - MockTools.exec(cmd) - } catch (e: Exception) { - e.printStackTrace() - } - } - - /** - * 终止与提供上下文指定的记录功能关联的过程。 - * - * @param上下文应用程序当前状态的上下文,用于访问资源和应用程序级操作 - */ - private fun killRecordProcess(context: Context) { - killRecordProcess( - 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); - } - } - - /** - * 从给定目录路径中检索并处理具有特定后缀的文件名。 - * 通过指定路径中的所有文件迭代,检查所需的后缀,然后提取 - * 来自其路径的文件名以进行进一步处理。 - * - * @param路径搜索文件的目录路径 - * @param houzhui要匹配和处理的文件的后缀 - * @throws IOException如果在访问文件时发生I/O错误 - */ - @Throws(IOException::class) - private fun getName(path: String, houzhui: String) { - //新建ArrayList - - var list: List? = 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!!) - } - } - } - } - - //将xapk格式变成zip格式,并进行解压 - private fun changeZip() { - val split = name!!.split(".xapk".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - //分割后选择保留那段 - 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) - private 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") - - 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) - private 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() - } - - //安装压缩包中的apk - @Throws(IOException::class) - private fun installApk() { - val apkpath = "$apk_path/$zip_name" - - Log.e("sss", apkpath) - //获取apk - 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 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 - } -}