Compare commits

..

41 Commits

Author SHA1 Message Date
yjj38 321e19aa16 chore: 添加 Firebase Crashlytics 配置并调整项目设置- 在项目中添加 Firebase Crashlytics 配置文件
-移除 misc.xml 中的 XML声明
- 在 StartJobService 中添加 onEvent 函数注释
2025-07-14 14:34:49 +08:00
yjj38 714b43f608 refactor(studyapp): 重构代码并添加注释
-为 MainActivity 和 StartJobService 中的关键方法添加了详细注释
- 删除了 MainActivity 中未使用的旧代码
- 新增 deployment.xml 和修改 misc.xml 以配置项目设置- 优化了 StartJobService 的执行逻辑,增加了错误处理和任务状态管理
2025-07-12 15:37:28 +08:00
Administrator 451d1ab3b5 packageName为空不上报信息 2025-07-10 17:36:29 +08:00
Administrator 8848423192 处理混淆 2025-07-10 14:02:56 +08:00
Administrator 4e090aca7e 时区从网页获取 2025-07-10 10:50:27 +08:00
yjj38 024a1db06c feat(proxy): 注释掉Clash代理组切换逻辑
- 在`MainActivity`的`startProxyVpn`方法中,注释掉了调用`ClashUtil.switchProxyGroup`切换代理组的代码。
2025-07-09 20:58:46 +08:00
Administrator ea0d4a55d2 代理切换 2025-07-09 18:03:30 +08:00
Administrator 424cb90898 混淆规则,失败重试间隔调整 2025-07-09 17:16:36 +08:00
Administrator c8d85a2b10 改机失败重新执行任务 2025-07-09 15:56:32 +08:00
Administrator 6667d0babd 改机失败重新执行任务 2025-07-09 15:51:23 +08:00
Administrator f6b4de5838 代理切换 2025-07-09 14:58:30 +08:00
Administrator b2162a97ca Merge remote-tracking branch 'origin/Added' into Added 2025-07-09 14:00:50 +08:00
Administrator 33259cf04e 代理切换 2025-07-09 14:00:40 +08:00
yjj38 a9b5ae4831 chore: 删除 ArmCloudApiClient 类并添加代码检查配置
- 删除了 app/src/main/java/com/example/studyapp/device/ArmCloudApiClient.java 文件
- 在项目根目录下添加了 .idea/inspectionProfiles/Project_Default.xml 文件,配置代码检查规则
2025-07-09 13:19:34 +08:00
Administrator b8acabc545 代理切换 2025-07-09 11:39:18 +08:00
Administrator 2ddd9a5f80 接口改机 2025-07-08 22:01:28 +08:00
yjj38 c63f13e54b feat(device): 新增 ArmCloudApiClient 用于与 ARM 云平台交互
- 实现了 `calculateSignature` 方法用于生成 API 请求签名。
- 新增 `updateAndroidModProperties` 方法,用于修改实例的安卓改机属性(需要重启生效)。
- 新增 `updateInstanceProperties` 方法,用于动态修改实例的属性信息(即时生效)。
- 定义了内部类 `PropertyItem` 用于封装属性名和属性值。
- 使用 OkHttp 进行网络请求,并集成了日志记录功能。
2025-07-08 20:01:14 +08:00
yjj38 db423f125c refactor(MainActivity): 优化国家切换逻辑并统一代码
- 将 switchCountry 方法改为返回当前国家代码
- 在切换代理组时直接使用 switchCountry 方法获取最新国家代码
- 统一国家代码获取方式,提高代码可维护性
2025-07-07 18:53:31 +08:00
yjj38 b003864b97 refactor(app): 优化设备信息上传逻辑
- 重构了 TaskUtil 类中的设备信息上传方法- 使用 Builder 模式构建 HttpUrl,提高了代码的可读性和可维护性
- 增加了对 packageName 参数的空值检查,避免不必要的查询参数
- 移除了冗余的异常抛出,改为直接返回,简化了错误处理逻辑
2025-07-07 18:30:32 +08:00
yjj38 530e2d6af2 refactor(MainActivity): 移除重复变量声明
- 删除 `scriptResult` 变量
- 调整了 `REQUEST_CODE_PERMISSIONS`, `TAG`, `PACKAGE_SCHEME`, `DEVICE_TYPE`, `CountryCode`, 和 `currentCountry` 变量的声明位置,以避免重复声明。
2025-07-07 16:09:10 +08:00
yjj38 069348cb43 refactor(main): 重构MainActivity并引入国家代码切换功能
- 重构了 MainActivity 的 onCreate 方法,将初始化逻辑拆分为多个独立方法,包括组件初始化、权限检查、网络检查和 WorkManager 设置。
- 引入了国家代码切换功能,允许在 "us" 和 "ru" 之间切换,并在启动 VPN 和获取设备信息时使用当前选择的国家代码。
- 优化了按钮初始化逻辑,使用 `setupButton` 方法统一处理按钮的查找和点击事件设置。
- 将部分硬编码字符串提取到 `strings.xml` 中。
- 移除了 ChangeDeviceInfoUtil 中的 `initialize` 方法,其功能已整合到 `getAddDeviceInfo` 中。
- 在执行主要任务逻辑 `executeLogic` 中,根据当前选择的国家代码来获取设备信息。
- 调整了部分日志记录的 TAG。
- 移除了 `getInstance` 方法,不再提供 MainActivity 的静态实例获取。
2025-07-07 16:02:17 +08:00
yjj38 f41f54c572 refactor(proxy): 移除代理检查国家功能并优化 VPN 启动逻辑
- 删除了 ClashUtil 类中的 checkCountryIsUS 方法,移除了检查国家是否为美国的功能
- 修改了 LoadDeviceWorker 类中的 startProxyVpn 方法,去除了返回值,简化了逻辑- 优化了 executeSingleLogic 方法的流程,移除了与代理检查国家相关的代码
2025-07-04 10:59:56 +08:00
yjj38 645c4b076a refactor(proxy): 重构代理工具类并添加地理位置信息获取功能- 移除了 ClashUtil 中的 checkCountryIsUS 方法
- 在 IpUtil 中添加了 fetchGeoInfo 方法,用于获取地理位置信息
- 更新了 MainActivity 中的代理启动逻辑
- 修改了 TaskUtil 中的设备信息上传逻辑,增加了 IP信息参数
2025-07-03 17:06:17 +08:00
yjj38 11ae6347cd refactor(main): 重构主函数并添加日志记录功能
- 新增 log 和 error 函数用于日志记录
- 重新封装 HTTP 请求和 API 处理逻辑
- 主函数增加错误处理和任务完成检查
- 添加定时器实现循环执行
2025-07-03 16:31:58 +08:00
yjj38 5ac9d2555f JDK升级到21,优化ClashUtil的代理切换和网络检查逻辑
- 项目的JDK版本从17升级到21。
- ClashUtil的`switchProxyGroup`方法改为同步执行,并增加了对HTTP响应状态码的检查。
- 新增`checkCountryIsUS`方法,用于通过ipinfo.io判断当前IP是否在美国,并在VPN启动后调用此方法进行验证。
- `LoadDeviceWorker`中的`startProxyVpn`方法现在会根据`checkCountryIsUS`的结果来决定是否继续执行后续操作。
- AutoJs脚本 (`main.js`) 更新,使用Promise和async/await来处理并行的HTTP请求,并分别调用ipv4.geojs.io的接口获取国家代码和详细地理位置信息。
2025-07-03 14:41:49 +08:00
yjj38 91a60683c5 refactor(proxy): 重构代理工具并添加地理位置检查功能
-重写 ClashUtil 类中的 switchProxyGroup 方法,使用同步调用替代异步回调
- 新增 checkCountryIsUS 方法,用于检查当前设备是否位于美国
- 修改 MainActivity 中的 startProxyVpn 方法,集成新的地理位置检查逻辑
-优化任务执行流程,确保仅在代理 VPN 启动成功且设备位于美国时执行后续操作
- 修复了一些潜在的资源泄露问题,提高了代码的健壮性
2025-07-03 14:12:02 +08:00
yjj38 fec032e52b 升级 JDK 版本到 21 2025-07-03 11:32:13 +08:00
Administrator c4f4b654f8 取消takeId 2025-06-28 15:15:08 +08:00
Administrator f6d15ec13b 打包配置 2025-06-28 14:38:05 +08:00
Administrator f51780943f device_info_upload增加takeId 2025-06-28 14:29:29 +08:00
Administrator e650ec29c6 优化异常处理 2025-06-28 11:27:26 +08:00
Administrator 87b70e2197 移除无用代码 2025-06-26 14:34:58 +08:00
Administrator 6d084c92e4 处理上传异常,压缩后卸载原apk 2025-06-26 14:08:01 +08:00
Administrator 827aaa21f8 2025-06-25 21:20:03 +08:00
Administrator be997233bd 打包所有apk文件 2025-06-25 21:18:15 +08:00
Administrator 3b83c6fc0b 2025-06-25 18:13:35 +08:00
Administrator 8de7349601 空包名不上传 2025-06-25 18:11:23 +08:00
Administrator 433419d1f3 . 2025-06-25 15:30:59 +08:00
Administrator 6f1bad5176 . 2025-06-25 15:29:08 +08:00
Administrator 17da31df13 . 2025-06-25 14:59:49 +08:00
Administrator 8f371c1a01 . 2025-06-25 11:40:18 +08:00
87 changed files with 2764 additions and 2449 deletions

3
.idea/.gitignore vendored
View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -1,120 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="GoogleStyle" />
</state>
</component>

View File

@ -4,149 +4,14 @@
<mappings /> <mappings />
<preferences /> <preferences />
</component> </component>
<component name="DBNavigator.Project.DataEditorManager">
<record-view-column-sorting-type value="BY_INDEX" />
<value-preview-text-wrapping value="true" />
<value-preview-pinned value="false" />
</component>
<component name="DBNavigator.Project.DatabaseAssistantManager"> <component name="DBNavigator.Project.DatabaseAssistantManager">
<assistants> <assistants />
<assistant-state connection-id="9e7d43d6-9c6a-47fa-b0b1-b14b18fadac4" default-profile-name="" selected-profile-name="" selected-model-name="" assistant-type="GENERIC" selected-action="SHOW_SQL" availability="UNAVAILABLE" acknowledgement="NONE">
<messages />
</assistant-state>
</assistants>
</component>
<component name="DBNavigator.Project.DatabaseBrowserManager">
<autoscroll-to-editor value="false" />
<autoscroll-from-editor value="true" />
<show-object-properties value="false" />
<loaded-nodes />
</component>
<component name="DBNavigator.Project.DatabaseConsoleManager">
<connection id="9e7d43d6-9c6a-47fa-b0b1-b14b18fadac4">
<console name="Connection" type="STANDARD" schema="gin_demo" session="Main" />
</connection>
</component>
<component name="DBNavigator.Project.DatabaseEditorStateManager">
<last-used-providers />
</component> </component>
<component name="DBNavigator.Project.DatabaseFileManager"> <component name="DBNavigator.Project.DatabaseFileManager">
<open-files /> <open-files />
</component> </component>
<component name="DBNavigator.Project.DatabaseSessionManager">
<connection id="9e7d43d6-9c6a-47fa-b0b1-b14b18fadac4" />
</component>
<component name="DBNavigator.Project.ExecutionManager">
<retain-sticky-names value="false" />
</component>
<component name="DBNavigator.Project.ObjectQuickFilterManager">
<last-used-operator value="EQUAL" />
<filters />
</component>
<component name="DBNavigator.Project.Settings"> <component name="DBNavigator.Project.Settings">
<connections> <connections />
<connection id="9e7d43d6-9c6a-47fa-b0b1-b14b18fadac4" active="true" signed="true">
<database>
<name value="Connection" />
<description value="" />
<database-type value="MYSQL" />
<config-type value="BASIC" />
<database-version value="9999.0" />
<driver-source value="BUNDLED" />
<driver-library value="" />
<driver value="" />
<url-type value="DATABASE" />
<host value="47.238.96.231" />
<port value="3306" />
<database value="gin_demo" />
<tns-folder value="" />
<tns-profile value="" />
<type value="USER_PASSWORD" />
<user value="root" />
<token-type value="" />
<token-config-file value="" />
<token-profile value="" />
<session-user value="" />
</database>
<properties>
<auto-commit value="false" />
</properties>
<ssh-settings>
<active value="false" />
<proxy-host value="" />
<proxy-port value="22" />
<proxy-user value="" />
<auth-type value="PASSWORD" />
<key-file value="" />
</ssh-settings>
<ssl-settings>
<active value="false" />
<certificate-authority-file value="" />
<client-certificate-file value="" />
<client-key-file value="" />
</ssl-settings>
<details>
<charset value="UTF-8" />
<session-management value="true" />
<ddl-file-binding value="true" />
<database-logging value="true" />
<connect-automatically value="true" />
<restore-workspace value="true" />
<restore-workspace-deep value="false" />
<environment-type value="default" />
<connectivity-timeout value="30" />
<idle-time-to-disconnect value="30" />
<idle-time-to-disconnect-pool value="5" />
<credential-expiry-time value="10" />
<max-connection-pool-size value="7" />
<alternative-statement-delimiter value="" />
</details>
<debugger>
<compile-dependencies value="true" />
<tcp-driver-tunneling value="false" />
<tcp-host-address value="" />
<tcp-port-from value="4000" />
<tcp-port-to value="4999" />
<debugger-type value="ASK" />
</debugger>
<object-filters hide-empty-schemas="false" hide-pseudo-columns="false" hide-audit-columns="false">
<object-filters />
<object-type-filter use-master-settings="true">
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="JAVA_CLASS" enabled="true" />
<object-type name="JAVA_INNER_CLASS" enabled="true" />
<object-type name="JAVA_FIELD" enabled="true" />
<object-type name="JAVA_METHOD" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
<object-type name="CREDENTIAL" enabled="true" />
<object-type name="AI_PROFILE" enabled="true" />
</object-type-filter>
</object-filters>
</connection>
</connections>
<browser-settings> <browser-settings>
<general> <general>
<display-mode value="TABBED" /> <display-mode value="TABBED" />
@ -561,8 +426,4 @@
</environment> </environment>
</general-settings> </general-settings>
</component> </component>
<component name="DBNavigator.Project.StatementExecutionManager">
<execution-variables />
<execution-variable-types />
</component>
</project> </project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false"> <component name="PublishConfigData">
<serverData> <serverData>
<paths name="腾讯云"> <paths name="腾讯云">
<serverdata> <serverdata>

View File

@ -4,14 +4,6 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-06-26T06:59:46.673021800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=8.217.74.194:1137;connection=a396d877" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GoogleJavaFormatSettings">
<option name="enabled" value="false" />
</component>
</project>

View File

@ -6,15 +6,13 @@
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" /> <option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$PROJECT_DIR$/../../../../Program Files/gradle-8.14" /> <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="gradleJvm" value="#JAVA_HOME" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
</set> </set>
</option> </option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>

View File

@ -1,6 +0,0 @@
{
"b3d893cf9de3a85a": [
"b3d893cf9de3a85a_bin.mt.plus.zip",
"dd597ef6-70c8-4765-94c3-a3262817b94c_b3d893cf9de3a85a_bin.mt.plus.zip"
]
}

View File

@ -1,5 +0,0 @@
{
"b3d893cf9de3a85a": [
"b3d893cf9de3a85a_bin.mt.plus.zip"
]
}

View File

@ -1,3 +0,0 @@
{
"bin.mt.plus": "b3d893cf9de3a85a_bin.mt.plus.zip"
}

View File

@ -1 +0,0 @@
{}

View File

@ -1,4 +0,0 @@
{
"bin.mt.plus": "b3d893cf9de3a85a_bin.mt.plus.zip",
"com.android.chrome": "b3d893cf9de3a85a_com.android.chrome.zip"
}

View File

@ -1 +0,0 @@
# domain path name value date

View File

@ -1,54 +0,0 @@
GET http://47.238.96.231:8112/get_package_info?androidId=b3d893cf9de3a85a
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> 2025-06-21T132008.200.json
###
GET http://47.238.96.231:8112/download_code_file?file_name=b3d893cf9de3a85a_bin.mt.plus.zip
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> b3d893cf9de3a85a_bin.mt.plus.zip
###
GET http://47.238.96.231:8112/download_code_file?file_name="b3d893cf9de3a85a_bin.mt.plus.zip"
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> 2025-06-21T124257.200.json
###
GET http://47.238.96.231:8112/get_package_info?androidId=b3d893cf9de3a85a
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> 2025-06-21T122848.200.json
###
GET http://47.238.96.231:8112/get_package_info?androidId=b3d893cf9de3a85a
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> 2025-06-21T115835.200.json
###
GET http://47.238.96.231:8112/get_package_info?androidId=b3d893cf9de3a85a
User-Agent: IntelliJ HTTP Client/IntelliJ IDEA 2025.1.2
Accept-Encoding: br, deflate, gzip, x-gzip
Accept: */*
<> 2025-06-21T105829.200.json
###

View File

@ -1,6 +1,84 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="UsePropertyAccessSyntax" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> <inspection_tool class="CodeBlock2Expr" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
<inspection_tool class="CssMissingSemicolon" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyCatchBlockJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyFinallyBlockJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyTryBlockJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IfCanBeSwitch" enabled="true" level="WARNING" enabled_by_default="true">
<option name="minimumBranches" value="3" />
<option name="suggestIntSwitches" value="true" />
<option name="suggestEnumSwitches" value="true" />
</inspection_tool>
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="JSFunctionExpressionToArrowFunction" enabled="true" level="INFORMATION" enabled_by_default="true" editorAttributes="INFORMATION_ATTRIBUTES" />
<inspection_tool class="JSRemoveUnnecessaryParentheses" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="JSStringConcatenationToES6Template" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ADDITIONAL_TAGS" value="TableName,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,Entity,createDate,description,TableName,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,TableName,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,createDate,description,Entity,createDate,description,Entity,createDate,description,createDate,description,TableName,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,Entity,createDate,description,createDate,description,createDate,description,Entity,createDate,description" />
</inspection_tool>
<inspection_tool class="LambdaParameterTypeCanBeSpecified" enabled="true" level="INFORMATION" enabled_by_default="true" editorAttributes="INFORMATION_ATTRIBUTES" />
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" />
</inspection_tool>
<inspection_tool class="MissingJavadoc" enabled="false" level="INFORMATION" enabled_by_default="false" editorAttributes="INFORMATION_ATTRIBUTES" />
<inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverlyLongLambda" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
</list>
</option>
</inspection_tool>
<inspection_tool class="RedundantExplicitVariableType" enabled="true" level="INFORMATION" enabled_by_default="true" editorAttributes="INFORMATION_ATTRIBUTES" />
<inspection_tool class="StaticCallOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticFieldReferenceOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="SuspiciousGetterSetter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThrowablePrintStackTrace" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
<scope name="Tests" level="WARNING" enabled="false" />
</inspection_tool>
<inspection_tool class="TypeScriptExplicitMemberType" enabled="true" level="INFORMATION" enabled_by_default="true" editorAttributes="INFORMATION_ATTRIBUTES" />
<inspection_tool class="UnnecessaryInheritDoc" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryJavaDocLink" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInlineLinkToSuper" value="false" />
</inspection_tool>
<inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnqualifiedMethodAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnterminatedStatementJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSemicolonAtEndOfBlock" value="true" />
</inspection_tool>
<inspection_tool class="VariableTypeCanBeExplicit" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
</profile> </profile>
</component> </component>

View File

@ -3,17 +3,10 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">
<option name="id" value="Android" /> <option name="id" value="Android" />
</component> </component>
<component name="VisualizationToolProject">
<option name="state">
<ProjectState>
<option name="scale" value="0.72" />
</ProjectState>
</option>
</component>
</project> </project>

BIN
app/agent_add.jks Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,22 +1,23 @@
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
} }
android { android {
namespace 'com.example.retention' namespace 'com.example.studyapp'
compileSdk 35 compileSdk 35
signingConfigs { signingConfigs {
release { release {
storeFile file('agent_retention.jks') storeFile file('agent_add.jks')
storePassword 'agent_retention' storePassword 'agent_add'
keyAlias 'agent_retention' keyAlias 'agent_add'
keyPassword 'agent_retention' keyPassword 'agent_add'
} }
} }
defaultConfig { defaultConfig {
applicationId "com.example.retention" applicationId "com.example.studyapp"
minSdk 24 minSdk 24
targetSdk 35 targetSdk 35
versionCode 1 versionCode 1
@ -62,6 +63,9 @@ android {
path file("src/main/cpp/CMakeLists.txt") path file("src/main/cpp/CMakeLists.txt")
} }
} }
kotlinOptions {
jvmTarget = '11'
}
applicationVariants.all { variant -> applicationVariants.all { variant ->
variant.outputs.all { output -> variant.outputs.all { output ->
@ -78,32 +82,34 @@ dependencies {
implementation libs.activity implementation libs.activity
implementation libs.constraintlayout implementation libs.constraintlayout
implementation libs.play.services.ads.identifier implementation libs.play.services.ads.identifier
implementation libs.core.ktx
testImplementation libs.junit testImplementation libs.junit
androidTestImplementation libs.ext.junit androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core androidTestImplementation libs.espresso.core
implementation 'androidx.work:work-runtime:2.9.0' implementation 'androidx.work:work-runtime:2.9.0'
// Retrofit // Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0' // implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//
// Gson JSON / // // Gson JSON /
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
//
// RxJava // // RxJava
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' // implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'org.nanohttpd:nanohttpd:2.3.1' implementation 'org.nanohttpd:nanohttpd:2.3.1'
// Mockito // Mockito
testImplementation 'org.mockito:mockito-core:5.4.0' testImplementation 'org.mockito:mockito-core:5.4.0'
// Android Instrumented Tests 使 Mockito // Android Instrumented Tests 使 Mockito
androidTestImplementation 'org.mockito:mockito-android:5.4.0' androidTestImplementation 'org.mockito:mockito-android:5.4.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.8.0' testImplementation 'org.mockito:mockito-inline:4.8.0'
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.12.0"))
implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation("com.squareup.okhttp3:okhttp")
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' implementation("com.squareup.okhttp3:logging-interceptor")
implementation ("com.google.code.gson:gson:2.10.1")
} }

View File

@ -1,16 +1,31 @@
# 保留 ArmCloudApiClient 及其所有公共方法和构造函数 # Add project specific ProGuard rules here.
-keep class com.example.retention.device.ArmCloudApiClient { # You can control the set of applied configuration files using the
public <init>(); # proguardFiles setting in build.gradle.
public *; #
} # For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# 保留 PropertyItem 内部类及其字段构造函数和 toJson 方法 # If your project uses WebView with JS, uncomment the following
-keep class com.example.retention.device.ArmCloudApiClient$PropertyItem { # and specify the fully qualified class name to the JavaScript interface
private java.lang.String propertiesName; # class:
private java.lang.String propertiesValue; #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public <init>(java.lang.String, java.lang.String); # public *;
public org.json.JSONObject toJson(); #}
}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# 保留 Gson 核心类
-keep class com.google.gson.** { *; }
-keep class com.google.gson.stream.** { *; }
# 保留所有注解
-keepattributes *Annotation*
-keepattributes Signature
# 保留枚举类 # 保留枚举类
-keepclassmembers enum * { -keepclassmembers enum * {
@ -18,10 +33,38 @@
public static ** valueOf(java.lang.String); public static ** valueOf(java.lang.String);
} }
# 保留使用 @SerializedName 注解的字段 # 保留所有模型类(根据你的包结构调整)
-keep class com.example.studyapp.pad.** { *; }
-keep class com.example.studyapp.task.** { *; }
-keep class com.example.studyapp.update.ApiResponse{ *; }
-keep class com.example.studyapp.update.ApiResponseList{ *; }
# 保留所有使用 @SerializedName 注解的字段
-keepclassmembers class * { -keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>; @com.google.gson.annotations.SerializedName <fields>;
} }
# 保留注解信息(根据需要启用) # 保留所有模型类的无参构造函数
# -keepattributes *Annotation* -keepclassmembers class com.example.studyapp.pad.** {
public <init>();
}
# 保留类型适配器
-keep class * extends com.google.gson.TypeAdapter {
public com.google.gson.TypeAdapter create(com.google.gson.Gson, com.google.gson.reflect.TypeToken);
}
# 保留 Gson 创建的类
-keep class com.google.gson.examples.android.model.** { *; }
-keepattributes Signature
# 保留 TypeToken 类及其子类
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken
-keep class sun.misc.Unsafe { *; }
# 保留注解信息
-keepattributes *Annotation*
# 保留 Kotlin 元数据(如果使用 Kotlin
-keepclassmembers class **$TypeToken { *; }

View File

@ -4,7 +4,7 @@
"type": "APK", "type": "APK",
"kind": "Directory" "kind": "Directory"
}, },
"applicationId": "com.example.retention", "applicationId": "com.example.studyapp",
"variantName": "release", "variantName": "release",
"elements": [ "elements": [
{ {
@ -13,7 +13,7 @@
"attributes": [], "attributes": [],
"versionCode": 1, "versionCode": 1,
"versionName": "1.0", "versionName": "1.0",
"outputFile": "app-com.example.retention-v1.0-1.apk" "outputFile": "app-com.example.studyapp-v1.0-1.apk"
} }
], ],
"elementType": "File", "elementType": "File",
@ -22,14 +22,14 @@
"minApi": 28, "minApi": 28,
"maxApi": 30, "maxApi": 30,
"baselineProfiles": [ "baselineProfiles": [
"baselineProfiles/1/app-com.example.retention-v1.0-1.dm" "baselineProfiles/1/app-com.example.studyapp-v1.0-1.dm"
] ]
}, },
{ {
"minApi": 31, "minApi": 31,
"maxApi": 2147483647, "maxApi": 2147483647,
"baselineProfiles": [ "baselineProfiles": [
"baselineProfiles/0/app-com.example.retention-v1.0-1.dm" "baselineProfiles/0/app-com.example.studyapp-v1.0-1.dm"
] ]
} }
], ],

View File

@ -1,4 +1,4 @@
package com.example.retention; package com.example.studyapp;
import android.content.Context; import android.content.Context;
@ -21,6 +21,6 @@ public class ExampleInstrumentedTest {
public void useAppContext() { public void useAppContext() {
// Context of the app under test. // Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.example.retention", appContext.getPackageName()); assertEquals("com.example.studyapp", appContext.getPackageName());
} }
} }

View File

@ -6,7 +6,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BIND_VPN_SERVICE" /> <uses-permission android:name="android.permission.BIND_VPN_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -39,13 +38,13 @@
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/box" android:icon="@mipmap/box"
android:label="@string/app_name" android:label="Script helper"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.StudyApp" android:theme="@style/Theme.StudyApp"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name="com.example.retention.MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -67,6 +66,14 @@
android:name="android.accessibilityservice" android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" /> android:resource="@xml/accessibility_service_config" />
</service> </service>
<service android:name="com.example.studyapp.job.StartJobService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"/>
<service android:name="com.example.studyapp.job.ScriptJobService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"/>
</application> </application>
<queries> <queries>

View File

@ -1,4 +0,0 @@
package com.example.retention
class ScriptRepository {
}

View File

@ -1,411 +0,0 @@
package com.example.retention.device;
import android.util.Log;
import com.example.retention.utils.LogFileUtil;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* @Time: 2025-06-08 17:06
* @Creator: 初屿贤
* @File: ArmCloudApiClient
* @Project: study.App
* @Description:
*/
public class ArmCloudApiClient {
private final OkHttpClient client;
private final String baseUrl = "https://openapi-hk.armcloud.net";
private final String accessKey = "gz8f1u0t63byzdu6ozbx8r5qs3e5lipt";
private final String secretKey = "3yc8c8bg1dym0zaiwjh867al";
public ArmCloudApiClient() {
this.client = new Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
}
private static final String ALGORITHM = "HmacSHA256";
public static String calculateSignature(String timestamp, String path, String body, String secretKey) throws Exception {
String stringToSign = timestamp + path + (body != null ? body : "");
Mac hmacSha256 = Mac.getInstance(ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), ALGORITHM);
hmacSha256.init(secretKeySpec);
byte[] hash = hmacSha256.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 修改实例安卓改机属性 静态设置安卓改机属性需要重启实例才能够生效一般用于修改设备信息 该接口与修改实例属性接口的区别在于生效时机该接口生效时间为每次开机初始化 设置实例属性后属性数据会持久化存储重启或重置实例无需再调用该接口
*
* @param padCode 实例 ID非空
* @param props 属性映射非空
* @param restart 是否自动重启
* @return 接口返回结果字符串
* @throws IOException 请求失败或网络错误
*/
public String updateAndroidModProperties(String padCode, Map<String, String> props, boolean restart) throws IOException {
// 参数校验
if (padCode == null || padCode.isEmpty()) {
throw new IllegalArgumentException("padCode 不能为空");
}
if (props == null) {
throw new IllegalArgumentException("props 不能为 null");
}
// 构造请求体
JSONObject json = new JSONObject();
try {
json.put("padCode", padCode);
json.put("props", new JSONObject(props));
json.put("restart", restart);
} catch (JSONException e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateAndroidModProperties: JSON error", e);
}
String jsonBody = json.toString();
// 生成时间戳
String timestamp = String.valueOf(System.currentTimeMillis());
String API_PATH = "/openapi/open/pad/updatePadAndroidProp";
String signature = "";
try {
signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey);
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateAndroidModProperties: Signature error", e);
}
RequestBody body = RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
jsonBody
);
Request request = new Request.Builder()
.url(baseUrl + API_PATH)
.addHeader("authver", "2.0")
.addHeader("x-ak", accessKey)
.addHeader("x-timestamp", timestamp)
.addHeader("x-sign", signature)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response);
}
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IOException("响应体为空");
}
return responseBody.string();
}
}
/**
* 修改实例属性 动态修改实例的属性信息包括系统属性和设置 实例需要处于开机状态该接口为即时生效
* <p>
* 示例 String[] padCodes = new String[]{"AC21020010001"}; List<PropertyItem> systemProps = Arrays.asList( new PropertyItem("ro.build.id", "QQ3A.200805.001")
* ); List<PropertyItem> oaidProps = Arrays.asList( new PropertyItem("oaid", "001") );
* <p>
* String response = client.updateInstanceProperties( padCodes, null, // modemPersistProps null, // modemProps null, // systemPersistProps systemProps,
* null, // settingProps oaidProps );
*/
public String updateInstanceProperties(
String[] padCodes,
List<PropertyItem> modemPersistProps,
List<PropertyItem> modemProps,
List<PropertyItem> systemPersistProps,
List<PropertyItem> systemProps,
List<PropertyItem> settingProps,
List<PropertyItem> oaidProps
) throws IOException {
if (padCodes == null || padCodes.length == 0) {
throw new IllegalArgumentException("padCodes 不能为空");
}
// 检查 padCodes 是否有重复项
Set<String> padCodeSet = new HashSet<>();
for (String code : padCodes) {
if (!padCodeSet.add(code)) {
throw new IllegalArgumentException("padCodes 包含重复项: " + code);
}
}
JSONObject json = new JSONObject();
try {
json.put("padCodes", new JSONArray(Arrays.asList(padCodes)));
putPropertyItems(json, "modemPersistPropertiesList", modemPersistProps);
putPropertyItems(json, "modemPropertiesList", modemProps);
putPropertyItems(json, "systemPersistPropertiesList", systemPersistProps);
putPropertyItems(json, "systemPropertiesList", systemProps);
putPropertyItems(json, "settingPropertiesList", settingProps);
putPropertyItems(json, "oaidPropertiesList", oaidProps);
} catch (JSONException e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: JSON 构建失败", e);
throw new IOException("JSON 构建失败", e);
}
String jsonBody = json.toString();
String timestamp = String.valueOf(System.currentTimeMillis());
String API_PATH = "/openapi/open/pad/updatePadProperties";
if (secretKey == null || secretKey.isEmpty()) {
throw new IllegalArgumentException("secretKey 不能为空");
}
String signature;
try {
signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey);
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 签名计算失败", e);
throw new IOException("签名计算失败", e);
}
RequestBody body = RequestBody.create(
MediaType.get("application/json; charset=utf-8"),
jsonBody
);
Request request = new Request.Builder()
.url(baseUrl + API_PATH)
.addHeader("authver", "2.0")
.addHeader("x-ak", accessKey)
.addHeader("x-timestamp", timestamp)
.addHeader("x-sign", signature)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response);
}
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IOException("响应体为空");
}
String responseBodyString = responseBody.string();
JSONObject responseJson = new JSONObject(responseBodyString);
// 校验返回码
if (responseJson.has("code")) {
int code = responseJson.getInt("code");
if (code != 200) {
String errorMsg = responseJson.optString("msg", "未知错误");
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null);
}
} else {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应中缺少 'code' 字段", null);
}
return responseBodyString;
} catch (JSONException e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应解析失败", e);
throw new IOException("响应解析失败", e);
}
}
private void putPropertyItems(JSONObject json, String key, List<PropertyItem> items) throws JSONException {
if (items != null && !items.isEmpty()) {
JSONArray array = new JSONArray();
for (PropertyItem item : items) {
array.put(item.toJson());
}
json.put(key, array);
}
}
public static class PropertyItem {
private String propertiesName;
private String propertiesValue;
public PropertyItem(String name, String value) {
this.propertiesName = name;
this.propertiesValue = value;
}
public JSONObject toJson() throws JSONException {
JSONObject json = new JSONObject();
json.put("propertiesName", propertiesName);
json.put("propertiesValue", propertiesValue);
return json;
}
}
/**
* 分页查询实例列表信息
*
* @param page 页码
* @param rows 每页条数
* @param armServerCode 服务器编号
* @param deviceCode 板卡编号
* @param padCodes 实例编号数组
* @param groupIds 实例分组ID数组
* @param idc 机房Id
* @return 匹配的 padCode 数组
* @throws IOException 请求失败或网络错误
*/
public String[] getInstanceListInfo(
int page,
int rows,
String armServerCode,
String deviceCode,
String[] padCodes,
Integer[] groupIds,
String idc) throws IOException {
JSONObject json = new JSONObject();
try {
json.put("page", page);
json.put("rows", rows);
if (armServerCode != null) {
json.put("armServerCode", armServerCode);
}
if (deviceCode != null) {
json.put("deviceCode", deviceCode);
}
if (padCodes != null && padCodes.length > 0) {
json.put("padCodes", new JSONArray(Arrays.asList(padCodes)));
}
if (groupIds != null && groupIds.length > 0) {
json.put("groupIds", new JSONArray(Arrays.asList(groupIds)));
}
if (idc != null) {
json.put("idc", idc);
}
} catch (JSONException e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: JSON 构建失败", e);
throw new IOException("JSON 构建失败", e);
}
String jsonBody = json.toString();
String timestamp = String.valueOf(System.currentTimeMillis());
String API_PATH = "/openapi/open/pad/infos";
if (secretKey == null || secretKey.isEmpty()) {
throw new IllegalArgumentException("secretKey 不能为空");
}
String signature;
try {
signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey);
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 签名计算失败", e);
throw new IOException("签名计算失败", e);
}
RequestBody body = RequestBody.create(
MediaType.get("application/json; charset=utf-8"),
jsonBody
);
Request request = new Request.Builder()
.url(baseUrl + API_PATH)
.addHeader("authver", "2.0")
.addHeader("x-ak", accessKey)
.addHeader("x-timestamp", timestamp)
.addHeader("x-sign", signature)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response);
}
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IOException("响应体为空");
}
String responseBodyString = responseBody.string();
JSONObject responseJson = new JSONObject(responseBodyString);
// 校验返回码
if (responseJson.has("code")) {
int code = responseJson.getInt("code");
if (code != 200) {
String errorMsg = responseJson.optString("msg", "未知错误");
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null);
throw new IOException("接口返回错误码: " + code + ", 错误信息: " + errorMsg);
}
} else {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应中缺少 'code' 字段", null);
throw new IOException("响应中缺少 'code' 字段");
}
// 提取 padCode 列表
JSONArray pageDataArray = responseJson.optJSONObject("data")
.optJSONArray("pageData");
if (pageDataArray == null || pageDataArray.length() == 0) {
LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 查询结果为空", null);
return new String[0];
}
int length = pageDataArray.length();
String[] padCodesResult = new String[length];
for (int i = 0; i < length; i++) {
JSONObject item = pageDataArray.getJSONObject(i);
if (!item.has("padCode")) {
LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 返回对象缺少 'padCode' 字段", null);
throw new IOException("返回对象缺少 'padCode' 字段");
}
padCodesResult[i] = item.getString("padCode");
}
return padCodesResult;
} catch (JSONException e) {
LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应解析失败", e);
throw new IOException("响应解析失败", e);
}
}
}

View File

@ -1,533 +0,0 @@
package com.example.retention.device;
import static com.example.retention.autoJS.AutoJsUtil.isAppInstalled;
import static com.example.retention.utils.LogFileUtil.logAndWrite;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.example.retention.device.ArmCloudApiClient.PropertyItem;
import com.example.retention.task.AfInfo;
import com.example.retention.task.BigoInfo;
import com.example.retention.task.DeviceInfo;
import com.example.retention.task.TaskUtil;
import com.example.retention.utils.ApkInstaller;
import com.example.retention.utils.HttpUtil;
import com.example.retention.utils.LogFileUtil;
import com.example.retention.utils.ShellUtils;
import com.example.retention.utils.ZipUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Method;
public class ChangeDeviceInfoUtil {
private static JSONObject bigoDeviceObject;
private static JSONObject afDeviceObject;
public static String packageName = "";
public static String zipName = "";
public static String buildBigoUrl(String country, int tag) {
return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!bigo.do")
.buildUpon()
.appendQueryParameter("country", country)
.appendQueryParameter("tag", String.valueOf(tag))
.toString();
}
public static String buildAfUrl(String country, int tag) {
return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!af.do")
.buildUpon()
.appendQueryParameter("country", country)
.appendQueryParameter("tag", String.valueOf(tag))
.toString();
}
// 创建一个线程池用于执行网络任务
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void initialize(String country, int tag, Context context, String androidId) {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Initializing device info...", null);
executorService.submit(() -> {
try {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Starting network requests...", null);
String bigoJson = fetchJsonSafely(buildBigoUrl(country, tag), "bigoJson");
String afJson = fetchJsonSafely(buildAfUrl(country, tag), "afJson");
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Received bigoJson: " + bigoJson, null);
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Received afJson: " + afJson, null);
fallBackToNetworkData(bigoJson, afJson);
logDeviceObjects();
processPackageInfo(TaskUtil.getPackageInfo(androidId), context);
} catch (IOException | JSONException e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during initialization", e);
} catch (Exception e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during initialization", e);
}
});
}
public static boolean getDeviceInfoSync(String taskId, String androidId) {
String response = "";
try {
response = executeQuerySafely(androidId, taskId);
} catch (Exception e) {
e.printStackTrace();
}
if (response == null || response.isBlank()) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during query", null);
return false;
}
try {
synchronized (ChangeDeviceInfoUtil.class) { // 防止并发访问
parseAndSetDeviceObjects(response);
}
return true;
} catch (Exception e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error parsing JSON", e);
return false;
}
}
private static String fetchJsonSafely(String url, String logKey) throws IOException {
String json = null;
try {
json = HttpUtil.requestGet(url);
if (json != null && !json.isEmpty()) {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Received " + logKey + ": " + json, null);
return json;
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "Empty or null response for: " + logKey + ", retrying...", null);
}
} catch (IOException e) {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "Error fetching " + logKey + ": " + e.getMessage() + ", retrying...", e);
}
// Retry once if the initial attempt failed
json = HttpUtil.requestGet(url);
if (json != null && !json.isEmpty()) {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Retry success for " + logKey + ": " + json, null);
return json;
} else {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Retry failed for " + logKey + ", response is still null or empty.", null);
throw new IOException("Failed to fetch valid JSON for " + logKey);
}
}
private static boolean isValidResponse(String response) {
return response != null && !response.isBlank() && !response.equals("{}\n")
&& !response.equals("{\"afDeviceObject\": null, \"bigoDeviceObject\": null, \"other\": null}")
&& response.trim().startsWith("{");
}
private static void parseAndSetDeviceObjects(String response) throws JSONException {
String cleanJson = response.trim();
if (cleanJson.startsWith("\"") && cleanJson.endsWith("\"")) {
cleanJson = cleanJson.substring(1, cleanJson.length() - 1).replace("\\\"", "\"");
}
JSONObject responseJson = new JSONObject(cleanJson);
bigoDeviceObject = responseJson.optJSONObject("bigoDeviceObject");
afDeviceObject = responseJson.optJSONObject("afDeviceObject");
packageName = responseJson.optString("package_name");
zipName = responseJson.optString("file_name");
}
private static void fallBackToNetworkData(String bigoJson, String afJson) throws JSONException {
bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device");
afDeviceObject = new JSONObject(afJson).optJSONObject("device");
}
private static void logDeviceObjects() {
LogFileUtil.logAndWrite(android.util.Log.INFO, LOG_TAG, "Final bigoDeviceObject: " + bigoDeviceObject, null);
LogFileUtil.logAndWrite(android.util.Log.INFO, LOG_TAG, "Final DeviceInfo: " + afDeviceObject, null);
}
public static void processPackageInfo(Map<String, String> packageInfo, Context context) {
if (packageInfo != null) {
for (Map.Entry<String, String> entry : packageInfo.entrySet()) {
String packageName = entry.getKey();
if (!isAppInstalled(packageName)) {
processPackage(packageName, entry.getValue(), context);
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "Package not installed: " + packageName, null);
}
}
}
}
public static boolean processPackageInfoWithDeviceInfo(String packageName, String zipName, Context context, String androidId, String taskId) {
if (!isAppInstalled(packageName)) {
return processPackage(packageName, zipName, context);
// TaskUtil.postDeviceInfo(androidId, taskId, packageName);
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "Package not installed: " + packageName, null);
return false;
}
}
private static boolean processPackage(String packageName, String zipName, Context context) {
try {
File filesDir = new File(context.getExternalFilesDir(null).getAbsolutePath());
File file = TaskUtil.downloadCodeFile(zipName, filesDir);
if (file != null && file.exists()) {
File destFile = new File(context.getCacheDir(), packageName);
if (destFile.exists()) {
TaskUtil.delFileSh(destFile.getAbsolutePath());
}
ZipUtils.unzip(file.getAbsolutePath(), destFile.getAbsolutePath());
if (destFile.exists()) {
installApk(destFile.getAbsolutePath());
}
TaskUtil.delFileSh(destFile.getAbsolutePath());
TaskUtil.delFileSh(file.getAbsolutePath());
LogFileUtil.logAndWrite(Log.DEBUG, LOG_TAG, "Processed package: " + packageName, null);
return true;
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "File download failed for package: " + packageName, null);
return false;
}
} catch (Exception e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error processing package: " + packageName, e);
return false;
}
}
public static boolean installApk(String apkFilePath) {
// 检查文件路径
if (apkFilePath == null || apkFilePath.trim().isEmpty()) {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "Invalid APK file path", null);
return false;
}
// 确保文件存在
File apkFile = new File(apkFilePath);
if (!apkFile.exists()) {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "APK file not found: " + apkFilePath, null);
return false;
}
boolean result = ApkInstaller.batchInstallWithRoot(apkFilePath);
if (result) {
Log.d("ShellUtils", "APK installed successfully!");
return true;
} else {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "Failed to install APK. Result: " + result, null);
return false;
}
}
private static final String LOG_TAG = "TaskUtil";
private static final String INIT_LOG_TEMPLATE = "initialize method called with parameters: Country: %s, Tag: %d, Android ID: %s";
private static final String CONTEXT_LOG_TEMPLATE = "Context instance: %s";
// 辅助方法执行网络请求
private static String fetchJson(String url) throws IOException {
return HttpUtil.requestGet(url);
}
// 辅助方法执行任务
private static String executeQuerySafely(String androidId, String taskId) {
return TaskUtil.execQueryTask(androidId, taskId);
}
public static void changeDeviceInfo(String current_pkg_name, Context context, ArmCloudApiClient client) {
final String B_PREFIX = current_pkg_name + ".";
final String U_PREFIX = current_pkg_name + "_";
if (bigoDeviceObject != null) {
// BIGO
BigoInfo bigoDevice = new BigoInfo();
bigoDevice.cpuClockSpeed = bigoDeviceObject.optString("cpu_clock_speed");
bigoDevice.gaid = bigoDeviceObject.optString("gaid");
bigoDevice.userAgent = bigoDeviceObject.optString("User-Agent");
bigoDevice.osLang = bigoDeviceObject.optString("os_lang");
bigoDevice.osVer = bigoDeviceObject.optString("os_ver");
bigoDevice.tz = bigoDeviceObject.optString("tz");
bigoDevice.systemCountry = bigoDeviceObject.optString("system_country");
bigoDevice.simCountry = bigoDeviceObject.optString("sim_country");
bigoDevice.romFreeIn = bigoDeviceObject.optLong("rom_free_in");
bigoDevice.resolution = bigoDeviceObject.optString("resolution");
bigoDevice.vendor = bigoDeviceObject.optString("vendor");
bigoDevice.batteryScale = bigoDeviceObject.optInt("bat_scale");
bigoDevice.net = bigoDeviceObject.optString("net");
bigoDevice.dpi = bigoDeviceObject.optInt("dpi");
bigoDevice.romFreeExt = bigoDeviceObject.optLong("rom_free_ext");
bigoDevice.dpiF = bigoDeviceObject.optString("dpi_f");
bigoDevice.cpuCoreNum = bigoDeviceObject.optInt("cpu_core_num");
TaskUtil.setBigoDevice(bigoDevice);
try {
Map<String, Object> bigoMap = new HashMap<>();
bigoMap.put(B_PREFIX + "system_country", bigoDevice.systemCountry);
bigoMap.put(B_PREFIX + "sim_country", bigoDevice.simCountry);
bigoMap.put(B_PREFIX + "rom_free_in", String.valueOf(bigoDevice.romFreeIn));
bigoMap.put(B_PREFIX + "resolution", bigoDevice.resolution);
bigoMap.put(B_PREFIX + "vendor", bigoDevice.vendor);
bigoMap.put(B_PREFIX + "battery_scale", String.valueOf(bigoDevice.batteryScale));
bigoMap.put(B_PREFIX + "os_lang", bigoDevice.osLang);
bigoMap.put(B_PREFIX + "net", bigoDevice.net);
bigoMap.put(B_PREFIX + "dpi", String.valueOf(bigoDevice.dpi));
bigoMap.put(B_PREFIX + "rom_free_ext", String.valueOf(bigoDevice.romFreeExt));
bigoMap.put(B_PREFIX + "dpi_f", bigoDevice.dpiF);
bigoMap.put(B_PREFIX + "cpu_core_num", String.valueOf(bigoDevice.cpuCoreNum));
bigoMap.put(B_PREFIX + "cpu_clock_speed", bigoDevice.cpuClockSpeed);
bigoMap.put(current_pkg_name + "_gaid", bigoDevice.gaid);
bigoMap.put(current_pkg_name + "_user_agent", bigoDevice.userAgent);
bigoMap.put(current_pkg_name + "_os_lang", bigoDevice.osLang);
bigoMap.put(current_pkg_name + "_os_ver", bigoDevice.osVer);
bigoMap.put(current_pkg_name + "_tz", bigoDevice.tz);
for (Map.Entry<String, Object> entry : bigoMap.entrySet()) {
callVCloudSettings_put(entry.getKey(), entry.getValue().toString(), context);
}
} catch (Throwable e) {
logAndWrite(android.util.Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred while changing device info", e);
throw e; // 保留原始异常类型
}
} else {
Log.w("ChangeDeviceInfoUtil", "bigoDeviceObject is null, skipping BIGO settings.");
}
if (afDeviceObject != null) {
AfInfo afDevice = new AfInfo();
afDevice.advertiserId = afDeviceObject.optString(".advertiserId");
afDevice.model = afDeviceObject.optString(".model");
afDevice.brand = afDeviceObject.optString(".brand");
afDevice.androidId = afDeviceObject.optString(".android_id");
afDevice.xPixels = afDeviceObject.optInt(".deviceData.dim.x_px");
afDevice.yPixels = afDeviceObject.optInt(".deviceData.dim.y_px");
afDevice.densityDpi = afDeviceObject.optInt(".deviceData.dim.d_dpi");
afDevice.country = afDeviceObject.optString(".country");
afDevice.batteryLevel = afDeviceObject.optString(".batteryLevel");
afDevice.stackInfo = Thread.currentThread().getStackTrace()[2].toString();
afDevice.product = afDeviceObject.optString(".product");
afDevice.network = afDeviceObject.optString(".network");
afDevice.langCode = afDeviceObject.optString(".lang_code");
afDevice.cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi");
afDevice.yDp = afDeviceObject.optInt(".deviceData.dim.ydp");
TaskUtil.setAfDevice(afDevice);
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.lang = afDeviceObject.optString(".lang");
deviceInfo.roProductBrand = afDeviceObject.optString("ro.product.brand", "");
deviceInfo.roProductModel = afDeviceObject.optString("ro.product.model", "");
deviceInfo.roProductManufacturer = afDeviceObject.optString("ro.product.manufacturer", "");
deviceInfo.roProductDevice = afDeviceObject.optString("ro.product.device", "");
deviceInfo.roProductName = afDeviceObject.optString("ro.product.name", "");
deviceInfo.roBuildVersionIncremental = afDeviceObject.optString("ro.build.version.incremental", "");
deviceInfo.roBuildFingerprint = afDeviceObject.optString("ro.build.fingerprint", "");
deviceInfo.roOdmBuildFingerprint = afDeviceObject.optString("ro.odm.build.fingerprint", "");
deviceInfo.roProductBuildFingerprint = afDeviceObject.optString("ro.product.build.fingerprint", "");
deviceInfo.roSystemBuildFingerprint = afDeviceObject.optString("ro.system.build.fingerprint", "");
deviceInfo.roSystemExtBuildFingerprint = afDeviceObject.optString("ro.system_ext.build.fingerprint", "");
deviceInfo.roVendorBuildFingerprint = afDeviceObject.optString("ro.vendor.build.fingerprint", "");
deviceInfo.roBuildPlatform = afDeviceObject.optString("ro.board.platform", "");
deviceInfo.persistSysCloudDrmId = afDeviceObject.optString("persist.sys.cloud.drm.id", "");
deviceInfo.persistSysCloudBatteryCapacity = afDeviceObject.optInt("persist.sys.cloud.battery.capacity", -1);
deviceInfo.persistSysCloudGpuGlVendor = afDeviceObject.optString("persist.sys.cloud.gpu.gl_vendor", "");
deviceInfo.persistSysCloudGpuGlRenderer = afDeviceObject.optString("persist.sys.cloud.gpu.gl_renderer", "");
deviceInfo.persistSysCloudGpuGlVersion = afDeviceObject.optString("persist.sys.cloud.gpu.gl_version", "");
deviceInfo.persistSysCloudGpuEglVendor = afDeviceObject.optString("persist.sys.cloud.gpu.egl_vendor", "");
deviceInfo.persistSysCloudGpuEglVersion = afDeviceObject.optString("persist.sys.cloud.gpu.egl_version", "");
TaskUtil.setDeviceInfo(deviceInfo);
try {
Map<String, Object> afMap = new HashMap<>();
afMap.put(B_PREFIX + "advertiserId", afDevice.advertiserId);
afMap.put(B_PREFIX + "model", afDevice.model);
afMap.put(B_PREFIX + "brand", afDevice.brand);
afMap.put(B_PREFIX + "android_id", afDevice.androidId);
afMap.put(B_PREFIX + "lang", afDevice.langCode);
afMap.put(B_PREFIX + "country", afDevice.country);
afMap.put(B_PREFIX + "batteryLevel", afDevice.batteryLevel);
afMap.put(B_PREFIX + "screen.optMetrics.stack", afDevice.stackInfo);
afMap.put(B_PREFIX + "product", afDevice.product);
afMap.put(B_PREFIX + "network", afDevice.network);
afMap.put(B_PREFIX + "cpu_abi", afDevice.cpuAbi);
afMap.put(B_PREFIX + "lang_code", afDevice.langCode);
for (Map.Entry<String, Object> entry : afMap.entrySet()) {
callVCloudSettings_put(entry.getKey(), entry.getValue().toString(), context);
}
callVCloudSettings_put(current_pkg_name + ".advertiserIdEnabled", "true", context);
JSONObject displayMetrics = new JSONObject();
displayMetrics.put("widthPixels", afDevice.xPixels);
displayMetrics.put("heightPixels", afDevice.yPixels);
displayMetrics.put("densityDpi", afDevice.densityDpi);
displayMetrics.put("yDp", afDevice.yDp);
callVCloudSettings_put("screen.device.displayMetrics", displayMetrics.toString(), context);
// 使用 ArmCloud API 设置系统属性
List<PropertyItem> systemProps = new ArrayList<>();
addProperty(systemProps, "ro.product.brand", deviceInfo.roProductBrand);
addProperty(systemProps, "ro.product.model", deviceInfo.roProductModel);
addProperty(systemProps, "ro.product.manufacturer", deviceInfo.roProductManufacturer);
addProperty(systemProps, "ro.product.device", deviceInfo.roProductDevice);
addProperty(systemProps, "ro.product.name", deviceInfo.roProductName);
addProperty(systemProps, "ro.build.version.incremental", deviceInfo.roBuildVersionIncremental);
addProperty(systemProps, "ro.build.fingerprint", deviceInfo.roBuildFingerprint);
addProperty(systemProps, "ro.odm.build.fingerprint", deviceInfo.roOdmBuildFingerprint);
addProperty(systemProps, "ro.product.build.fingerprint", deviceInfo.roProductBuildFingerprint);
addProperty(systemProps, "ro.system.build.fingerprint", deviceInfo.roSystemBuildFingerprint);
addProperty(systemProps, "ro.system_ext.build.fingerprint", deviceInfo.roSystemExtBuildFingerprint);
addProperty(systemProps, "ro.vendor.build.fingerprint", deviceInfo.roVendorBuildFingerprint);
addProperty(systemProps, "ro.board.platform", deviceInfo.roBuildPlatform);
// 调用接口更新实例属性
try {
String[] padCodes = client.getInstanceListInfo(1, 100, null, null, null, null, null);
LogFileUtil.logAndWrite(Log.DEBUG, "ChangeDeviceInfoUtil", "padCodes: " + Arrays.toString(padCodes), null);
String response = client.updateInstanceProperties(
padCodes,
null, // modemPersistProps
null, // modemProps
null, // systemPersistProps
systemProps,
null, // settingProps
null // oaidProps
);
Log.d("ChangeDeviceInfoUtil", "updateInstanceProperties response: " + response);
} catch (IOException e) {
Log.e("ChangeDeviceInfoUtil", "Failed to update instance properties", e);
}
} catch (Throwable e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", e);
}
} else {
Log.w("ChangeDeviceInfoUtil", "afDeviceObject is null, skipping AF settings.");
}
}
private static void addProperty(List<PropertyItem> list, String name, String value) {
if (value != null && !value.isEmpty()) {
list.add(new PropertyItem(name, value));
}
}
private static void execRootCmdIfNotEmpty(String cmd, String value) {
if (value != null && !value.isEmpty()) {
String result = ShellUtils.execRootCmdAndGetResult(cmd + value);
if (result == null) {
Log.e("ChangeDeviceInfoUtil", "Failed to execute shell command: " + cmd + value + ", result was empty or null.");
} else {
Log.d("ChangeDeviceInfoUtil", "Successfully executed command: " + cmd + value + ", output: " + result);
}
}
}
private static void callVCloudSettings_put(String key, String value, Context context) {
if (context == null) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Context cannot be null", null);
throw new IllegalArgumentException("Context cannot be null");
}
if (key == null || key.isEmpty()) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null);
throw new IllegalArgumentException("Key cannot be null or empty");
}
if (value == null) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Value cannot be null", null);
throw new IllegalArgumentException("Value cannot be null");
}
try {
// 获取类对象
Class<?> clazz = Class.forName("android.provider.VCloudSettings$Global");
Method putStringMethod = clazz.getDeclaredMethod("putString", ContentResolver.class, String.class, String.class);
putStringMethod.setAccessible(true);
// 调用方法
putStringMethod.invoke(null, context.getContentResolver(), key, value);
Log.d("Debug", "putString executed successfully.");
} catch (ClassNotFoundException e) {
logAndWrite(Log.WARN, "ChangeDeviceInfoUtil", "Class not found: android.provider.VCloudSettings$Global. This may not be supported on this device.", e);
} catch (NoSuchMethodException e) {
logAndWrite(Log.WARN, "ChangeDeviceInfoUtil", "Method not found: android.provider.VCloudSettings$Global.putString. This may not be supported on this", e);
} catch (InvocationTargetException e) {
Throwable cause = e.getTargetException();
if (cause instanceof SecurityException) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause);
} else {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause);
}
} catch (Exception e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Unexpected error during putString invocation", e);
}
}
public static void resetChangedDeviceInfo(String current_pkg_name, Context context) {
try {
Native.setBootId("00000000000000000000000000000000");
} catch (Exception e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in reset", e);
}
if (!ShellUtils.hasRootAccess()) {
LogFileUtil.logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Root access is required to execute system property changes", null);
return;
}
ShellUtils.execRootCmd("cmd settings2 delete global global_android_id");
ShellUtils.execRootCmd("cmd settings2 delete global pm_list_features");
ShellUtils.execRootCmd("cmd settings2 delete global pm_list_libraries");
ShellUtils.execRootCmd("cmd settings2 delete global anticheck_pkgs");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_android_id");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_adb_enabled");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_development_settings_enabled");
ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_vendor \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer \"\"");
// 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_version \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_vendor \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version \"\"");
ShellUtils.execRootCmd("setprop ro.product.brand Vortex");
ShellUtils.execRootCmd("setprop ro.product.model HD65_Select");
ShellUtils.execRootCmd("setprop ro.product.manufacturer Vortex");
ShellUtils.execRootCmd("setprop ro.product.device HD65_Select");
ShellUtils.execRootCmd("setprop ro.product.name HD65_Select");
ShellUtils.execRootCmd("setprop ro.build.version.incremental 20240306");
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");
}
}

View File

@ -1,19 +0,0 @@
package com.example.retention.task;
public class AfInfo {
public String advertiserId;
public String model;
public String brand;
public String androidId;
public int xPixels;
public int yPixels;
public int densityDpi;
public String country;
public String batteryLevel;
public String stackInfo;
public String product;
public String network;
public String langCode;
public String cpuAbi;
public long yDp;
}

View File

@ -1,22 +0,0 @@
package com.example.retention.task;
// 使用 JSON 库动态生成 JSON 请求体 (使用 Gson 示例)
public class BigoInfo {
public String cpuClockSpeed;
public String gaid;
public String userAgent;
public String osLang;
public String osVer;
public String tz;
public String systemCountry;
public String simCountry;
public long romFreeIn;
public String resolution;
public String vendor;
public int batteryScale;
public String net;
public long dpi;
public long romFreeExt;
public String dpiF;
public long cpuCoreNum;
}

View File

@ -1,26 +0,0 @@
package com.example.retention.task;
public class DeviceInfo {
public String lang;
public String roProductBrand;
public String roProductModel;
public String roProductManufacturer;
public String roProductDevice;
public String roProductName;
public String roBuildVersionIncremental;
public String roBuildFingerprint;
public String roOdmBuildFingerprint;
public String roProductBuildFingerprint;
public String roSystemBuildFingerprint;
public String roSystemExtBuildFingerprint;
public String roVendorBuildFingerprint;
public String roBuildPlatform;
public String persistSysCloudDrmId;
public int persistSysCloudBatteryCapacity;
public String persistSysCloudGpuGlVendor;
public String persistSysCloudGpuGlRenderer;
public String persistSysCloudGpuGlVersion;
public String persistSysCloudGpuEglVendor;
public String persistSysCloudGpuEglVersion;
}

View File

@ -1,58 +0,0 @@
package com.example.retention.utils;
import static com.example.retention.utils.ZipUtils.getAllApkFiles;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ApkInstaller {
public static boolean batchInstallWithRoot(String dirPath) {
// 获取APK文件
List<File> apkFiles = getAllApkFiles(dirPath);
return installSplitApks(apkFiles);
}
private static boolean installSplitApks( List<File> apkFiles) {
// 确保base.apk在第一位
File baseApk = null;
List<File> otherApks = new ArrayList<>();
for (File apk : apkFiles) {
if (apk.getName().contains("base.apk")) {
baseApk = apk;
} else {
otherApks.add(apk);
}
}
if (baseApk == null) {
Log.d("TAG", "installSplitApks: 没有 base apk");
return false;
}
// 构建安装命令
StringBuilder cmd = new StringBuilder("pm install \"")
.append(baseApk.getAbsolutePath()).append("\"");
for (File apk : otherApks) {
cmd.append(" \"").append(apk.getAbsolutePath()).append("\"");
}
Log.d("TAG", "installSplitApks: "+cmd);
// 执行命令
String result = ShellUtils.execRootCmdAndGetResult(cmd.toString());
if (result != null && result.contains("Success")) {
Log.d("TAG", "installSplitApks: install success");
return true;
} else {
Log.d("TAG", "installSplitApks: install failed");
return false;
}
}
}

View File

@ -1,78 +0,0 @@
package com.example.retention.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Environment;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class Utils {
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
Network network = connectivityManager.getActiveNetwork();
if (network != null) {
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
}
}
return false;
}
public static boolean isAppInstalled(Context context, String packageName) {
try {
PackageManager pm = context.getPackageManager();
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
public static boolean isPackageInstalled(Context context, String packageName) {
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(0);
for (ApplicationInfo packageInfo : packages) {
Log.d("TAG", "isPackageInstalled: "+packageInfo.packageName);
if (packageInfo.packageName.equals(packageName)) {
return true;
}
}
return false;
}
public static void writePackageName(String packageName){
File file = new File(Environment.getExternalStorageDirectory(),
"script/packagesname.txt");
File parentDir = file.getParentFile();
if (parentDir != null && !parentDir.exists()) {
boolean dirsCreated = parentDir.mkdirs();
if (!dirsCreated) {
Log.e("FileWrite", "Failed to create directories: " + parentDir);
return;
}
}
LogFileUtil.logAndWrite(Log.INFO,"TAG", "writePackageName: "+packageName, null);
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(file))) {
bos.write(packageName.getBytes(StandardCharsets.UTF_8));
bos.flush(); // 确保数据写入磁盘
} catch (IOException e) {
Log.e("FileWrite", "Failed to write package name: " + packageName, e);
// 6. 可以考虑添加重试机制或通知用户
}
}
}

View File

@ -1,87 +0,0 @@
package com.example.retention.utils;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipUtils {
/**
* 解压ZIP文件到指定目录
* @param zipFile ZIP文件路径
* @param destDir 目标目录路径
* @throws IOException 如果解压失败
*/
public static void unzip(String zipFile, String destDir) throws IOException {
File dir = new File(destDir);
// 创建目标目录如果不存在
if (!dir.exists()) {
if (!dir.mkdirs()) {
throw new IOException("Failed to create directory: " + destDir);
}
}
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)))) {
ZipEntry ze;
byte[] buffer = new byte[8192];
int count;
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
File file = new File(destDir, fileName);
// 创建必要的父目录
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
if (!parent.mkdirs()) {
throw new IOException("Failed to create parent directory: " + parent);
}
}
if (ze.isDirectory()) {
if (!file.mkdirs()) {
throw new IOException("Failed to create directory: " + file);
}
} else {
try (FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
while ((count = zis.read(buffer)) != -1) {
bos.write(buffer, 0, count);
}
}
}
zis.closeEntry();
}
}
}
public static List<File> getAllApkFiles(String dirPath) {
List<File> apkFiles = new ArrayList<>();
File dir = new File(dirPath);
if (!dir.exists() || !dir.isDirectory()) {
Log.e("APK", "目录不存在或不是目录: " + dirPath);
return apkFiles;
}
File[] files = dir.listFiles();
if (files == null) return apkFiles;
for (File file : files) {
if (file.isFile() && file.getName().toLowerCase().endsWith(".apk")) {
apkFiles.add(file);
}
}
return apkFiles;
}
}

View File

@ -1,101 +0,0 @@
package com.example.retention.worker;
import static com.example.retention.utils.Utils.isNetworkAvailable;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.work.CoroutineWorker;
import androidx.work.WorkerParameters;
import com.example.retention.MainActivity;
import com.example.retention.autoJS.AutoJsUtil;
import com.example.retention.config.CountryCode;
import com.example.retention.device.ChangeDeviceInfoUtil;
import com.example.retention.proxy.ClashUtil;
import com.example.retention.utils.LogFileUtil;
import com.example.retention.utils.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import kotlin.coroutines.Continuation;
public class LoadDeviceWorker extends CoroutineWorker {
private String androidId = "FyZqWrStUvOpKlMn";
private Context context;
public LoadDeviceWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.context = context;
}
/**
* 这段代码是LoadDeviceWorker类中的doWork方法用于执行后台任务其功能如下 生成唯一的taskId调用ChangeDeviceInfoUtil.getDeviceInfoSync获取设备信息 获取packageName和zipName并记录日志
* 如果获取设备信息成功且packageName和zipName非空则调用processPackageInfoWithDeviceInfo处理包信息 若处理成功执行executeSingleLogic方法 否则记录获取设备信息失败的日志 最后返回任务执行结果为成功
*/
@Override
public @Nullable Object doWork(@NotNull Continuation<? super Result> continuation) {
String taskId = UUID.randomUUID().toString();
boolean result = ChangeDeviceInfoUtil.getDeviceInfoSync(taskId, androidId);
String packageName = ChangeDeviceInfoUtil.packageName;
String zipName = ChangeDeviceInfoUtil.zipName;
LogFileUtil.logAndWrite(Log.INFO, "TAG", "doWork: " + result + " " + packageName + " " + zipName, null);
if (result && !TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(zipName)) {
boolean isSuccess = ChangeDeviceInfoUtil.processPackageInfoWithDeviceInfo(packageName, zipName, getApplicationContext(), androidId, taskId);
if (isSuccess) {
executeSingleLogic(context, packageName);
}
} else {
LogFileUtil.logAndWrite(Log.INFO, "TAG", "doWork: get Device info false", null);
}
return Result.success();
}
/**
* 这段代码是LoadDeviceWorker类中的executeSingleLogic方法用于执行特定逻辑其功能如下 检查传入的packageName是否为空或空字符串如果是则记录日志并退出方法 记录代理未激活并调用startProxyVpn方法启动VPN
* 记录更改设备信息并调用ChangeDeviceInfoUtil.changeDeviceInfo方法 记录运行AutoJs脚本并调用Utils.writePackageName和AutoJsUtil.runAutojsScript方法
*/
public void executeSingleLogic(Context context, String packageName) {
if (packageName == null || packageName.isEmpty()) {
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "executeSingleLogic: Package name is empty", null);
return;
}
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "executeSingleLogic: Proxy not active, starting VPN", null);
startProxyVpn(context);
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "executeSingleLogic: Changing device info", null);
ChangeDeviceInfoUtil.changeDeviceInfo(packageName, context, MainActivity.armClient);
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "executeSingleLogic: Running AutoJs script", null);
Utils.writePackageName(packageName);
AutoJsUtil.runAutojsScript(context);
}
private void startProxyVpn(Context context) {
if (!isNetworkAvailable(context)) {
Toast.makeText(context, "Network is not available", Toast.LENGTH_SHORT).show();
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Network is not available.", null);
}
// if (!(context instanceof Activity)) {
// Toast.makeText(context, "Context must be an Activity", Toast.LENGTH_SHORT).show();
// LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Context is not an Activity.",null);
// return;
// }
try {
ClashUtil.startProxy(context);
ClashUtil.switchProxyWithPort(CountryCode.switchCountry());
// ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170");
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Failed to start VPN", e);
Toast.makeText(context, "Failed to start VPN: " +
(e.getMessage() != null ? e.getMessage() : "Unknown error"),
Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -1,17 +1,19 @@
package com.example.retention; package com.example.studyapp;
import static com.example.retention.task.TaskUtil.infoUpload; import static com.example.studyapp.task.TaskUtil.uploadFile;
import static com.example.retention.utils.Utils.isNetworkAvailable;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.net.Uri; import android.net.Uri;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build; import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@ -20,28 +22,26 @@ import android.Manifest;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest; import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import com.example.retention.R; import com.example.studyapp.autoJS.AutoJsUtil;
import com.example.retention.autoJS.AutoJsUtil; import com.example.studyapp.device.ChangeDeviceInfoUtil;
import com.example.retention.config.CountryCode;
import com.example.retention.device.ArmCloudApiClient;
import com.example.retention.device.ChangeDeviceInfoUtil;
import com.example.retention.proxy.ClashUtil; import com.example.studyapp.job.StartJobService;
import com.example.retention.service.MyAccessibilityService; import com.example.studyapp.proxy.ClashUtil;
import com.example.retention.task.TaskUtil; import com.example.studyapp.service.MyAccessibilityService;
import com.example.retention.utils.LogFileUtil; import com.example.studyapp.task.TaskUtil;
import com.example.retention.utils.ShellUtils; import com.example.studyapp.utils.CountryCode;
import com.example.retention.worker.CheckAccessibilityWorker; import com.example.studyapp.utils.LogFileUtil;
import com.example.retention.worker.LoadDeviceWorker; import com.example.studyapp.utils.ShellUtils;
import com.example.studyapp.worker.CheckAccessibilityWorker;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.UUID; import java.util.UUID;
@ -52,14 +52,14 @@ import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private static WeakReference<MainActivity> instance;
private static final int REQUEST_CODE_STORAGE_PERMISSION = 1; private static final int REQUEST_CODE_STORAGE_PERMISSION = 1;
private static final int ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE = 1001; private static final int ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE = 1001;
public static ExecutorService executorService; public static ExecutorService executorService;
public static ArmCloudApiClient armClient;
// 假设我们从配置文件中提取出了以下 name 项数据仅为部分示例数据 // 假设我们从配置文件中提取出了以下 name 项数据仅为部分示例数据
private final String[] proxyNames = { private final String[] proxyNames = {
"mr", "sr", "bq", "ml", "ht", "ga", "mk", "by", "pr", "hr", "hu", "mr", "sr", "bq", "ml", "ht", "ga", "mk", "by", "pr", "hr", "hu",
@ -77,6 +77,11 @@ public class MainActivity extends AppCompatActivity {
"ge", "ps" "ge", "ps"
}; };
private static final int REQUEST_CODE_PERMISSIONS = 100;
private static final String TAG = "MainActivity";
private static final String PACKAGE_SCHEME = "package:";
// 初始化 ExecutorService // 初始化 ExecutorService
private void initializeExecutorService() { private void initializeExecutorService() {
if (executorService == null || executorService.isShutdown()) { if (executorService == null || executorService.isShutdown()) {
@ -90,150 +95,188 @@ public class MainActivity extends AppCompatActivity {
} }
} }
private static final int REQUEST_CODE_PERMISSIONS = 100; /**
* 获取 Android 设备的 ANDROID_ID
*
* @param context 应用上下文
* @return 设备的 ANDROID_ID若无法获取则返回 null
*/
private String getAndroidId(Context context) {
return "FyZqWrStUvOpKlMn";
}
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
initializeBasicComponents();
checkAndRequestPermissions();
checkNetworkConnection();
setupWorkManager();
initializeButtons();
}
private void initializeBasicComponents() {
LogFileUtil.initialize(this); LogFileUtil.initialize(this);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
instance = new WeakReference<>(this);
logInfo("onCreate: Initializing application"); LogFileUtil.logAndWrite(Log.INFO, TAG, "onCreate: Initializing application", null);
initializeExecutorService(); initializeExecutorService();
System.setProperty("java.library.path", getApplicationInfo().nativeLibraryDir); System.setProperty("java.library.path", getApplicationInfo().nativeLibraryDir);
}
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) checkStoragePermission();
!= PackageManager.PERMISSION_GRANTED) {
requestStoragePermission();
}
} else { } else {
checkAllFilesAccessPermission();
}
}
private void checkStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_STORAGE_PERMISSION
);
}
}
private void checkAllFilesAccessPermission() {
if (VERSION.SDK_INT >= VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) { if (!Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName())); intent.setData(Uri.parse(PACKAGE_SCHEME + getPackageName()));
startActivityForResult(intent, ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE); startActivityForResult(intent, ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE);
} }
} }
}
private void checkNetworkConnection() {
if (!isNetworkAvailable(this)) { if (!isNetworkAvailable(this)) {
showToast("网络不可用"); LogFileUtil.logAndWrite(Log.ERROR, TAG, "Network not available, closing app.", null);
logError("Network not available, closing app."); Toast.makeText(this, R.string.network_unavailable, Toast.LENGTH_SHORT).show();
finish(); finish();
return;
}
logInfo("onCreate: Setting up work manager");
schedulePeriodicWorkIfNotExists();
logInfo("onCreate: Setting up UI components");
setupButton(R.id.run_script_button, v -> {
try {
AutoJsUtil.runAutojsScript(this);
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "runAutojsScript: " + e.getMessage(), e);
showToast("执行脚本失败");
}
});
setupButton(R.id.connectVpnButton, v -> startProxyVpn(this));
setupButton(R.id.disconnectVpnButton, v -> ClashUtil.stopProxy(this));
setupButton(R.id.switchVpnButton, v -> ClashUtil.switchProxyGroup("GLOBAL", "us", "http://127.0.0.1:6170"));
armClient = new ArmCloudApiClient();
String currentPackage = getPackageName();
ShellUtils.execRootCmdAndGetResult("pm grant " + currentPackage + " android.permission.INTERACT_ACROSS_USERS");
ShellUtils.execRootCmdAndGetResult("pm grant " + currentPackage + " android.permission.WRITE_SECURE_SETTINGS");
setupButton(R.id.modifyDeviceInfoButton, v -> ChangeDeviceInfoUtil.changeDeviceInfo(currentPackage, this, armClient));
setupButton(R.id.resetDeviceInfoButton, v -> ChangeDeviceInfoUtil.resetChangedDeviceInfo(currentPackage, this));
setupButton(R.id.execute_button, v -> {
((Button) v).setEnabled(false);
startLoadWork();
});
setupButton(R.id.stop_execute_button, v -> {
WorkManager.getInstance(this).cancelAllWorkByTag(WORK_TAG);
Button executeButton = findViewById(R.id.execute_button);
if (executeButton != null) {
executeButton.setEnabled(true);
}
});
}
private void requestStoragePermission() {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_STORAGE_PERMISSION
);
}
private void schedulePeriodicWorkIfNotExists() {
WorkManager.getInstance(this).getWorkInfosForUniqueWorkLiveData("CheckAccessibilityWorker")
.observe(this, workInfos -> {
if (workInfos == null || workInfos.isEmpty()) {
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(CheckAccessibilityWorker.class, 15, TimeUnit.MINUTES)
.addTag("CheckAccessibilityWorker")
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"CheckAccessibilityWorker",
ExistingPeriodicWorkPolicy.REPLACE,
workRequest
);
}
});
}
private void setupButton(int resId, View.OnClickListener listener) {
Button button = findViewById(resId);
if (button != null) {
button.setOnClickListener(listener);
} else {
logInfo("Button not found: " + resId);
} }
} }
private static String WORK_TAG = "LOAD_WORK"; private void setupWorkManager() {
LogFileUtil.logAndWrite(Log.INFO, TAG, "onCreate: Setting up work manager", null);
/** PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
* 这段代码的功能是启动一个周期性后台任务 创建一个周期为30分钟的PeriodicWorkRequest执行LoadDeviceWorker类的任务 设置初始延迟为0秒添加任务标签WORK_TAG 使用WorkManager将任务加入队列准备执行 CheckAccessibilityWorker.class,
*/ 15,
private void startLoadWork() { TimeUnit.MINUTES
PeriodicWorkRequest workRequest = new PeriodicWorkRequest. ).build();
Builder(LoadDeviceWorker.class, 30, TimeUnit.MINUTES)
.setInitialDelay(0, TimeUnit.SECONDS)
.addTag(WORK_TAG)
.build();
WorkManager.getInstance(this).enqueue(workRequest); WorkManager.getInstance(this).enqueue(workRequest);
} }
private void initializeButtons() {
LogFileUtil.logAndWrite(Log.INFO, TAG, "onCreate: Setting up UI components", null);
String androidId = getAndroidId(this);
String taskId = UUID.randomUUID().toString();
setupButton(R.id.run_script_button, v -> executeRunScript(androidId));
setupButton(R.id.connectVpnButton, v ->executorService.submit(() -> {
startProxyVpn(MainActivity.this);
}));
setupButton(R.id.disconnectVpnButton, v -> ClashUtil.stopProxy(this));
setupButton(R.id.switchVpnButton, v -> {
ClashUtil.switchProxyGroup("GLOBAL", CountryCode.switchCountry(), "http://127.0.0.1:6170");
});
setupButton(R.id.resetDeviceInfoButton,
v -> ChangeDeviceInfoUtil.resetChangedDeviceInfo(getPackageName(), this));
setupExecutionButtons(androidId, taskId);
}
private void setupButton(@IdRes int buttonId, View.OnClickListener listener) {
Button button = findViewById(buttonId);
if (button != null) {
button.setOnClickListener(listener);
} else {
LogFileUtil.logAndWrite(Log.WARN, TAG, "Button not found: " + buttonId, null);
Toast.makeText(this, R.string.button_not_found, Toast.LENGTH_SHORT).show();
}
}
private void executeRunScript(String androidId) {
initializeExecutorService();
executorService.submit(() -> {
try {
TaskUtil.uploadFile(MainActivity.this, androidId, "wsj.reader_sp");
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* 这段代码的功能是
* 初始化执行按钮和停止执行按钮
* 为执行按钮设置点击事件点击后禁用按钮显示任务执行中提示并调用执行逻辑方法
* 调用 setupStopExecutionButton 方法配置停止按钮的行为
*/
private void setupExecutionButtons(String androidId, String taskId) {
Button executeButton = findViewById(R.id.execute_button);
Button stopExecuteButton = findViewById(R.id.stop_execute_button);
if (executeButton != null) {
executeButton.setOnClickListener(v -> {
executeButton.setEnabled(false);
Toast.makeText(this, R.string.task_executing, Toast.LENGTH_SHORT).show();
executeLogic(androidId, taskId);
});
}
setupStopExecutionButton(stopExecuteButton, executeButton);
}
private void setupStopExecutionButton(Button stopExecuteButton, Button executeButton) {
if (stopExecuteButton != null) {
stopExecuteButton.setOnClickListener(v -> {
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdownNow();
ClashUtil.stopProxy(this);
AutoJsUtil.stopAutojsScript(this);
executeButton.setEnabled(true);
}
});
} else {
Toast.makeText(this, R.string.stop_button_not_found, Toast.LENGTH_SHORT).show();
}
}
/**
*记录Start execution日志
* 检查设备是否有网络连接若无则直接返回
* 注册脚本结果接收器
* 触发作业服务事件
*/
private void executeLogic(String androidId, String taskId) {
LogFileUtil.logAndWrite(Log.INFO, TAG, "Start execution", null);
if (!isNetworkAvailable(this)) {
return;
}
AutoJsUtil.registerScriptResultReceiver(this);
StartJobService.Companion.onEvent(this);
}
public static final LinkedBlockingQueue<String> scriptResultQueue = new LinkedBlockingQueue<>(1); public static final LinkedBlockingQueue<String> scriptResultQueue = new LinkedBlockingQueue<>(1);
public static final Object taskLock = new Object(); // 任务逻辑锁
private void startProxyVpn(Context context) { private void startProxyVpn(Context context) {
if (!isNetworkAvailable(context)) { if (!isNetworkAvailable(context)) {
Toast.makeText(context, "Network is not available", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "Network is not available", Toast.LENGTH_SHORT).show();
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Network is not available.", null); LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Network is not available.", null);
return;
}
if (!(context instanceof Activity)) {
Toast.makeText(context, "Context must be an Activity", Toast.LENGTH_SHORT).show();
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Context is not an Activity.", null);
return;
} }
try { try {
ClashUtil.startProxy(context); ClashUtil.startProxy(context);
ClashUtil.switchProxyWithPort(CountryCode.switchCountry()); ClashUtil.switchProxyWithPort(CountryCode.switchCountry());
// ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170"); //ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170");
} catch (Exception e) { } catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", "startProxyVpn: Failed to start VPN", e); LogFileUtil.logAndWrite(Log.ERROR, TAG, "startProxyVpn: Failed to start VPN", e);
Toast.makeText(context, "Failed to start VPN: " + Toast.makeText(context, "Failed to start VPN: " +
(e.getMessage() != null ? e.getMessage() : "Unknown error"), (e.getMessage() != null ? e.getMessage() : "Unknown error"),
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
@ -315,6 +358,7 @@ public class MainActivity extends AppCompatActivity {
protected void onDestroy() { protected void onDestroy() {
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "onDestroy: Cleaning up resources", null); LogFileUtil.logAndWrite(Log.INFO, "MainActivity", "onDestroy: Cleaning up resources", null);
super.onDestroy(); super.onDestroy();
instance.clear();
if (AutoJsUtil.scriptResultReceiver != null) { if (AutoJsUtil.scriptResultReceiver != null) {
unregisterReceiver(AutoJsUtil.scriptResultReceiver); unregisterReceiver(AutoJsUtil.scriptResultReceiver);
AutoJsUtil.scriptResultReceiver = null; AutoJsUtil.scriptResultReceiver = null;
@ -322,22 +366,22 @@ public class MainActivity extends AppCompatActivity {
if (executorService != null) { if (executorService != null) {
executorService.shutdown(); executorService.shutdown();
} }
synchronized (taskLock) {
taskLock.notifyAll();
}
} }
private void logInfo(String message) { private boolean isNetworkAvailable(Context context) {
LogFileUtil.logAndWrite(Log.INFO, "MainActivity", message, null); ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
} if (connectivityManager != null) {
Network network = connectivityManager.getActiveNetwork();
private void logError(String message) { if (network != null) {
LogFileUtil.logAndWrite(Log.ERROR, "MainActivity", message, null); NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
} return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
private void logWarn(String message) { }
LogFileUtil.logAndWrite(Log.WARN, "MainActivity", message, null); }
} return false;
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} }
} }

View File

@ -1,6 +1,7 @@
package com.example.retention.autoJS; package com.example.studyapp.autoJS;
import static com.example.retention.task.TaskUtil.downloadCodeFile; import static com.example.studyapp.MainActivity.taskLock;
import static com.example.studyapp.task.TaskUtil.downloadCodeFile;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -14,10 +15,10 @@ import android.widget.Toast;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.example.retention.MainActivity; import com.example.studyapp.MainActivity;
import com.example.retention.utils.LogFileUtil; import com.example.studyapp.job.ScriptJobService;
import com.example.retention.utils.ShellUtils; import com.example.studyapp.utils.LogFileUtil;
import com.example.studyapp.utils.ShellUtils;
import java.io.File; import java.io.File;
public class AutoJsUtil { public class AutoJsUtil {
@ -31,8 +32,11 @@ public class AutoJsUtil {
// 检查脚本文件 // 检查脚本文件
LogFileUtil.logAndWrite(android.util.Log.INFO, "AutoJsUtil", "-------脚本运行开始:--------" + count++,null); LogFileUtil.logAndWrite(android.util.Log.INFO, "AutoJsUtil", "-------脚本运行开始:--------" + count++,null);
File scriptDir = new File(Environment.getExternalStorageDirectory(), "script");//todo File scriptDir = new File(Environment.getExternalStorageDirectory(), "script");//todo
scriptDir.delete(); File oldFile = new File(scriptDir, "main.js");
File scriptFile = downloadCodeFile("mainold.js", scriptDir);//todo if (oldFile.exists()){
oldFile.delete();
}
File scriptFile = downloadCodeFile("main.js", scriptDir);
if (scriptFile == null || !scriptFile.exists()) { if (scriptFile == null || !scriptFile.exists()) {
runOnUiThread(() -> Toast.makeText(context, "下载脚本文件失败", Toast.LENGTH_SHORT).show()); runOnUiThread(() -> Toast.makeText(context, "下载脚本文件失败", Toast.LENGTH_SHORT).show());
LogFileUtil.logAndWrite(android.util.Log.ERROR, "AutoJsUtil", "下载脚本文件失败",null); LogFileUtil.logAndWrite(android.util.Log.ERROR, "AutoJsUtil", "下载脚本文件失败",null);
@ -68,11 +72,15 @@ public class AutoJsUtil {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, "MainActivity", "----脚本运行结束通知一次------; 当前线程:" + Thread.currentThread().getName(), null); LogFileUtil.logAndWrite(android.util.Log.DEBUG, "MainActivity", "----脚本运行结束通知一次------; 当前线程:" + Thread.currentThread().getName(), null);
String scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY); String scriptResult = intent.getStringExtra(SCRIPT_RESULT_KEY);
try { try {
MainActivity.scriptResultQueue.put(scriptResult); // 将结果加入队列 if (scriptResult == null){
scriptResult = "";
}
ScriptJobService.Companion.onEvent(context, scriptResult);
// MainActivity.scriptResultQueue.put(scriptResult); // 将结果加入队列
LogFileUtil.logAndWrite(android.util.Log.DEBUG, "MainActivity", "----收到result------;" + scriptResult, null); LogFileUtil.logAndWrite(android.util.Log.DEBUG, "MainActivity", "----收到result------;" + scriptResult, null);
// AutoJsUtil.flag = true; // 唤醒 // AutoJsUtil.flag = true; // 唤醒
// taskLock.notifyAll(); // taskLock.notifyAll();
} catch (InterruptedException e) { } catch (Exception e) {
Thread.currentThread().interrupt(); // 处理中断 Thread.currentThread().interrupt(); // 处理中断
} }
} }
@ -127,7 +135,7 @@ public class AutoJsUtil {
} }
} }
private static final String AUTOJS_SCRIPT_FINISHED_ACTION = "org.autojs.SCRIPT_FINISHED_CACHE"; private static final String AUTOJS_SCRIPT_FINISHED_ACTION = "org.autojs.SCRIPT_FINISHED";
private static final String SCRIPT_RESULT_KEY = "result"; private static final String SCRIPT_RESULT_KEY = "result";
public static void stopAutojsScript(Context context) { public static void stopAutojsScript(Context context) {

View File

@ -1,8 +1,8 @@
package com.example.retention.config; package com.example.studyapp.config;
import android.content.Context; import android.content.Context;
import com.example.retention.utils.LogFileUtil; import com.example.studyapp.utils.LogFileUtil;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;

View File

@ -0,0 +1,564 @@
package com.example.studyapp.device;
import static com.example.studyapp.autoJS.AutoJsUtil.isAppInstalled;
import static com.example.studyapp.utils.LogFileUtil.logAndWrite;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.example.studyapp.task.AfInfo;
import com.example.studyapp.task.BigoInfo;
import com.example.studyapp.task.DeviceInfo;
import com.example.studyapp.task.TaskUtil;
import com.example.studyapp.utils.HttpUtil;
import com.example.studyapp.utils.LogFileUtil;
import com.example.studyapp.utils.ShellUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Method;
public class ChangeDeviceInfoUtil {
private static JSONObject bigoDeviceObject;
private static JSONObject afDeviceObject;
public static String buildBigoUrl(String country, int tag) {
return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!bigo.do")
.buildUpon()
.appendQueryParameter("country", country)
.appendQueryParameter("tag", String.valueOf(tag))
.toString();
}
public static String buildAfUrl(String country, int tag) {
return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!af.do")
.buildUpon()
.appendQueryParameter("country", country)
.appendQueryParameter("tag", String.valueOf(tag))
.toString();
}
// 创建一个线程池用于执行网络任务
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void getAddDeviceInfo(String country, int tag, LoadDeviceCallback callback){
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Initializing device info...", null);
executorService.submit(() -> {
try {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG, "Starting network requests...", null);
String bigoJson = fetchJsonSafely(buildBigoUrl(country, tag), "bigoJson");
String afJson = fetchJsonSafely(buildAfUrl(country, tag), "afJson");
JSONObject bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device");
JSONObject afDeviceObject = new JSONObject(afJson).optJSONObject("device");
callback.onLoadDeviceInfo(bigoDeviceObject, afDeviceObject);
} catch (Exception e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during initialization", e);
callback.onLoadDeviceError("Error occurred during initialization");
}
});
}
public static void getDeviceInfo(String taskId, String androidId) {
if (taskId == null || androidId == null || taskId.isBlank() || androidId.isBlank()) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Invalid task",null);
return;
}
executorService.submit(() -> {
String response = "";
try{
response = executeQuerySafely(androidId, taskId);
} catch (Exception e) {
e.printStackTrace();
}
if (response == null || response.isBlank()) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during query", null);
return;
}
if (isValidResponse(response)) {
try {
synchronized (ChangeDeviceInfoUtil.class) { // 防止并发访问
parseAndSetDeviceObjects(response);
}
} catch (JSONException e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error parsing JSON", e);
}
} else {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error occurred during query",null);
}
});
}
private static String fetchJsonSafely(String url, String logKey) throws IOException {
String json = null;
int attemptCount = 0;
while (true) {
attemptCount++;
try {
json = HttpUtil.requestGet(url);
if (json != null && !json.isEmpty()) {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, LOG_TAG,
"Received " + logKey + " after " + attemptCount + " attempt(s): " + json, null);
return json;
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG,
"Empty or null response for: " + logKey + ", retrying in 3 seconds...", null);
}
} catch (IOException e) {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG,
"Error fetching " + logKey + " (attempt " + attemptCount + "): " + e.getMessage() + ", retrying in 3 seconds...", e);
}
try {
Thread.sleep(3000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG,
"Retry interrupted for " + logKey, ie);
throw new IOException("Retry interrupted for " + logKey, ie);
}
}
}
private static boolean isValidResponse(String response) {
return response != null && !response.isBlank() && !response.equals("{}\n")
&& !response.equals("{\"afDeviceObject\": null, \"bigoDeviceObject\": null, \"other\": null}")
&& response.trim().startsWith("{");
}
private static void parseAndSetDeviceObjects(String response) throws JSONException {
String cleanJson = response.trim();
if (cleanJson.startsWith("\"") && cleanJson.endsWith("\"")) {
cleanJson = cleanJson.substring(1, cleanJson.length() - 1).replace("\\\"", "\"");
}
JSONObject responseJson = new JSONObject(cleanJson);
bigoDeviceObject = responseJson.optJSONObject("bigoDeviceObject");
afDeviceObject = responseJson.optJSONObject("afDeviceObject");
}
private static void fallBackToNetworkData(String bigoJson, String afJson) throws JSONException {
bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device");
afDeviceObject = new JSONObject(afJson).optJSONObject("device");
}
private static void logDeviceObjects() {
LogFileUtil.logAndWrite(android.util.Log.INFO, LOG_TAG, "Final bigoDeviceObject: " + bigoDeviceObject, null);
LogFileUtil.logAndWrite(android.util.Log.INFO, LOG_TAG, "Final DeviceInfo: " + afDeviceObject, null);
}
private static void processPackageInfo(Map<String, String> packageInfo, Context context) {
if (packageInfo != null) {
for (Map.Entry<String, String> entry : packageInfo.entrySet()) {
String packageName = entry.getKey();
if (!isAppInstalled(packageName)) {
processPackage(packageName, entry.getValue(), context);
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "Package not installed: " + packageName, null);
}
}
}
}
private static void processPackage(String packageName, String zipName, Context context) {
try {
File filesDir = new File(context.getExternalFilesDir(null).getAbsolutePath());
File file = TaskUtil.downloadCodeFile(zipName, filesDir);
if (file != null && file.exists()) {
File destFile = new File(context.getCacheDir(), packageName+"_download"+".apk");
if (destFile.exists()) {
TaskUtil.delFileSh(destFile.getAbsolutePath());
}
TaskUtil.unZip(destFile, file);
if (destFile.exists()) {
installApk(destFile.getAbsolutePath());
}
TaskUtil.delFileSh(destFile.getAbsolutePath());
TaskUtil.delFileSh(file.getAbsolutePath());
LogFileUtil.logAndWrite(Log.DEBUG, LOG_TAG, "Processed package: " + packageName, null);
} else {
LogFileUtil.logAndWrite(android.util.Log.WARN, LOG_TAG, "File download failed for package: " + packageName, null);
}
} catch (Exception e) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, LOG_TAG, "Error processing package: " + packageName, e);
}
}
public static boolean installApk(String apkFilePath) {
// 检查文件路径
if (apkFilePath == null || apkFilePath.trim().isEmpty()) {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "Invalid APK file path", null);
return false;
}
// 确保文件存在
File apkFile = new File(apkFilePath);
if (!apkFile.exists()) {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "APK file not found: " + apkFilePath, null);
return false;
}
// 构造安装命令
String command = "pm install " + apkFilePath;
// 执行命令并获取结果
String result = ShellUtils.execRootCmdAndGetResult(command);
if (result != null && result.contains("Success")) {
Log.d("ShellUtils", "APK installed successfully!");
return true;
} else {
LogFileUtil.logAndWrite(Log.ERROR, "ShellUtils", "Failed to install APK. Result: " + result, null);
return false;
}
}
private static final String LOG_TAG = "TaskUtil";
private static final String INIT_LOG_TEMPLATE = "initialize method called with parameters: Country: %s, Tag: %d, Android ID: %s";
private static final String CONTEXT_LOG_TEMPLATE = "Context instance: %s";
// 辅助方法执行网络请求
private static String fetchJson(String url) throws IOException {
return HttpUtil.requestGet(url);
}
// 辅助方法执行任务
private static String executeQuerySafely(String androidId, String taskId) {
return TaskUtil.execQueryTask(androidId,taskId);
}
public static void changeDeviceInfo(String current_pkg_name, Context context, JSONObject bigoDeviceObject, JSONObject afDeviceObject) {
try{
BigoInfo bigoDevice;
if (bigoDeviceObject != null) {
// BIGO
String cpuClockSpeed = bigoDeviceObject.optString("cpu_clock_speed");
String gaid = bigoDeviceObject.optString("gaid");
String userAgent = bigoDeviceObject.optString("User-Agent");
String osLang = bigoDeviceObject.optString("os_lang");
String osVer = bigoDeviceObject.optString("os_ver");
String tz = bigoDeviceObject.optString("tz");
String systemCountry = bigoDeviceObject.optString("system_country");
String simCountry = bigoDeviceObject.optString("sim_country");
long romFreeIn = bigoDeviceObject.optLong("rom_free_in");
String resolution = bigoDeviceObject.optString("resolution");
String vendor = bigoDeviceObject.optString("vendor");
int batteryScale = bigoDeviceObject.optInt("bat_scale");
// String model = deviceObject.optString("model");
String net = bigoDeviceObject.optString("net");
int dpi = bigoDeviceObject.optInt("dpi");
long romFreeExt = bigoDeviceObject.optLong("rom_free_ext");
String dpiF = bigoDeviceObject.optString("dpi_f");
int cpuCoreNum = bigoDeviceObject.optInt("cpu_core_num");
bigoDevice = new BigoInfo();
bigoDevice.cpuClockSpeed = cpuClockSpeed;
bigoDevice.gaid = gaid;
bigoDevice.userAgent = userAgent;
bigoDevice.osLang = osLang;
bigoDevice.osVer = osVer;
bigoDevice.tz = tz;
bigoDevice.systemCountry = systemCountry;
bigoDevice.simCountry = simCountry;
bigoDevice.romFreeIn = romFreeIn;
bigoDevice.resolution = resolution;
bigoDevice.vendor = vendor;
bigoDevice.batteryScale = batteryScale;
bigoDevice.net = net;
bigoDevice.dpi = dpi;
bigoDevice.romFreeExt = romFreeExt;
bigoDevice.dpiF = dpiF;
bigoDevice.cpuCoreNum = cpuCoreNum;
TaskUtil.setBigoDevice(bigoDevice);
try {
callVCloudSettings_put(current_pkg_name + ".system_country", systemCountry, context);
callVCloudSettings_put(current_pkg_name + ".sim_country", simCountry, context);
callVCloudSettings_put(current_pkg_name + ".rom_free_in", String.valueOf(romFreeIn), context);
callVCloudSettings_put(current_pkg_name + ".resolution", resolution, context);
callVCloudSettings_put(current_pkg_name + ".vendor", vendor, context);
callVCloudSettings_put(current_pkg_name + ".battery_scale", String.valueOf(batteryScale), context);
callVCloudSettings_put(current_pkg_name + ".os_lang", osLang, context);
// callVCloudSettings_put(current_pkg_name + ".model", model, context);
callVCloudSettings_put(current_pkg_name + ".net", net, context);
callVCloudSettings_put(current_pkg_name + ".dpi", String.valueOf(dpi), context);
callVCloudSettings_put(current_pkg_name + ".rom_free_ext", String.valueOf(romFreeExt), context);
callVCloudSettings_put(current_pkg_name + ".dpi_f", dpiF, context);
callVCloudSettings_put(current_pkg_name + ".cpu_core_num", String.valueOf(cpuCoreNum), context);
callVCloudSettings_put(current_pkg_name + ".cpu_clock_speed", cpuClockSpeed, context);
callVCloudSettings_put(current_pkg_name + "_gaid", gaid, context);
// **User-Agent**
callVCloudSettings_put(current_pkg_name + "_user_agent", userAgent, context);
// **os_lang**系统语言
callVCloudSettings_put(current_pkg_name + "_os_lang", osLang, context);
// **os_ver**
callVCloudSettings_put(current_pkg_name + "_os_ver", osVer, context);
// **tz** (时区)
callVCloudSettings_put(current_pkg_name + "_tz", tz, context);
} catch (Throwable e) {
logAndWrite(android.util.Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred while changing device info", e);
throw new RuntimeException("Error occurred in changeDeviceInfo", e);
}
bigoDeviceObject = null;
}
DeviceInfo deviceInfo;
AfInfo afDevice;
if (afDeviceObject != null) {
String advertiserId = afDeviceObject.optString(".advertiserId");
String model = afDeviceObject.optString(".model");
String brand = afDeviceObject.optString(".brand");
String androidId = afDeviceObject.optString(".android_id");
int xPixels = afDeviceObject.optInt(".deviceData.dim.x_px");
int yPixels = afDeviceObject.optInt(".deviceData.dim.y_px");
int densityDpi = afDeviceObject.optInt(".deviceData.dim.d_dpi");
String country = afDeviceObject.optString(".country");
String batteryLevel = afDeviceObject.optString(".batteryLevel");
String stackInfo = Thread.currentThread().getStackTrace()[2].toString();
String product = afDeviceObject.optString(".product");
String network = afDeviceObject.optString(".network");
String langCode = afDeviceObject.optString(".lang_code");
String cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi");
int yDp = afDeviceObject.optInt(".deviceData.dim.ydp");
afDevice = new AfInfo();
afDevice.advertiserId = advertiserId;
afDevice.model = model;
afDevice.brand = brand;
afDevice.androidId = androidId;
afDevice.xPixels = xPixels;
afDevice.yPixels = yPixels;
afDevice.densityDpi = densityDpi;
afDevice.country = country;
afDevice.batteryLevel = batteryLevel;
afDevice.stackInfo = stackInfo;
afDevice.product = product;
afDevice.network = network;
afDevice.langCode = langCode;
afDevice.cpuAbi = cpuAbi;
afDevice.yDp = yDp;
TaskUtil.setAfDevice(afDevice);
String lang = afDeviceObject.optString(".lang");
String ro_product_brand = afDeviceObject.optString("ro.product.brand", "");
String ro_product_model = afDeviceObject.optString("ro.product.model", "");
String ro_product_manufacturer = afDeviceObject.optString("ro.product.manufacturer", "");
String ro_product_device = afDeviceObject.optString("ro.product.device", "");
String ro_product_name = afDeviceObject.optString("ro.product.name", "");
String ro_build_version_incremental = afDeviceObject.optString("ro.build.version.incremental", "");
String ro_build_fingerprint = afDeviceObject.optString("ro.build.fingerprint", "");
String ro_odm_build_fingerprint = afDeviceObject.optString("ro.odm.build.fingerprint", "");
String ro_product_build_fingerprint = afDeviceObject.optString("ro.product.build.fingerprint", "");
String ro_system_build_fingerprint = afDeviceObject.optString("ro.system.build.fingerprint", "");
String ro_system_ext_build_fingerprint = afDeviceObject.optString("ro.system_ext.build.fingerprint", "");
String ro_vendor_build_fingerprint = afDeviceObject.optString("ro.vendor.build.fingerprint", "");
String ro_build_platform = afDeviceObject.optString("ro.board.platform", "");
String persist_sys_cloud_drm_id = afDeviceObject.optString("persist.sys.cloud.drm.id", "");
int persist_sys_cloud_battery_capacity = afDeviceObject.optInt("persist.sys.cloud.battery.capacity", -1);
String persist_sys_cloud_gpu_gl_vendor = afDeviceObject.optString("persist.sys.cloud.gpu.gl_vendor", "");
String persist_sys_cloud_gpu_gl_renderer = afDeviceObject.optString("persist.sys.cloud.gpu.gl_renderer", "");
String persist_sys_cloud_gpu_gl_version = afDeviceObject.optString("persist.sys.cloud.gpu.gl_version", "");
String persist_sys_cloud_gpu_egl_vendor = afDeviceObject.optString("persist.sys.cloud.gpu.egl_vendor", "");
String persist_sys_cloud_gpu_egl_version = afDeviceObject.optString("persist.sys.cloud.gpu.egl_version", "");
String global_android_id = afDeviceObject.optString(".android_id", "");
String anticheck_pkgs = afDeviceObject.optString(".anticheck_pkgs", "");
String pm_list_features = afDeviceObject.optString(".pm_list_features", "");
String pm_list_libraries = afDeviceObject.optString(".pm_list_libraries", "");
String system_http_agent = afDeviceObject.optString("system.http.agent", "");
String webkit_http_agent = afDeviceObject.optString("webkit.http.agent", "");
String com_fk_tools_pkgInfo = afDeviceObject.optString(".pkg_info", "");
String appsflyerKey = afDeviceObject.optString(".appsflyerKey", "");
String appUserId = afDeviceObject.optString(".appUserId", "");
String disk = afDeviceObject.optString(".disk", "");
String operator = afDeviceObject.optString(".operator", "");
String cell_mcc = afDeviceObject.optString(".cell.mcc", "");
String cell_mnc = afDeviceObject.optString(".cell.mnc", "");
String date1 = afDeviceObject.optString(".date1", "");
String date2 = afDeviceObject.optString(".date2", "");
String bootId = afDeviceObject.optString("BootId", "");
deviceInfo = new DeviceInfo();
deviceInfo.lang = lang;
deviceInfo.roProductBrand = ro_product_brand;
deviceInfo.roProductModel = ro_product_model;
deviceInfo.roProductManufacturer = ro_product_manufacturer;
deviceInfo.roProductDevice = ro_product_device;
deviceInfo.roProductName = ro_product_name;
deviceInfo.roBuildVersionIncremental = ro_build_version_incremental;
deviceInfo.roBuildFingerprint = ro_build_fingerprint;
deviceInfo.roOdmBuildFingerprint = ro_odm_build_fingerprint;
deviceInfo.roProductBuildFingerprint = ro_product_build_fingerprint;
deviceInfo.roSystemBuildFingerprint = ro_system_build_fingerprint;
deviceInfo.roSystemExtBuildFingerprint = ro_system_ext_build_fingerprint;
deviceInfo.roVendorBuildFingerprint = ro_vendor_build_fingerprint;
deviceInfo.roBuildPlatform = ro_build_platform;
deviceInfo.persistSysCloudDrmId = persist_sys_cloud_drm_id;
deviceInfo.persistSysCloudBatteryCapacity = persist_sys_cloud_battery_capacity;
deviceInfo.persistSysCloudGpuGlVendor = persist_sys_cloud_gpu_gl_vendor;
deviceInfo.persistSysCloudGpuGlRenderer = persist_sys_cloud_gpu_gl_renderer;
deviceInfo.persistSysCloudGpuGlVersion = persist_sys_cloud_gpu_gl_version;
deviceInfo.persistSysCloudGpuEglVendor = persist_sys_cloud_gpu_egl_vendor;
deviceInfo.persistSysCloudGpuEglVersion = persist_sys_cloud_gpu_egl_version;
TaskUtil.setDeviceInfo(deviceInfo);
try {
callVCloudSettings_put(current_pkg_name + ".advertiserId", advertiserId, context);
callVCloudSettings_put(current_pkg_name + ".model", model, context);
callVCloudSettings_put(current_pkg_name + ".brand", brand, context);
callVCloudSettings_put(current_pkg_name + ".android_id", androidId, context);
callVCloudSettings_put(current_pkg_name + ".lang", lang, context);
callVCloudSettings_put(current_pkg_name + ".country", country, context);
callVCloudSettings_put(current_pkg_name + ".batteryLevel", batteryLevel, context);
callVCloudSettings_put(current_pkg_name + "_screen.optMetrics.stack", stackInfo, context);
callVCloudSettings_put(current_pkg_name + ".product", product, context);
callVCloudSettings_put(current_pkg_name + ".network", network, context);
callVCloudSettings_put(current_pkg_name + ".cpu_abi", cpuAbi, context);
callVCloudSettings_put(current_pkg_name + ".lang_code", langCode, context);
// **广告标识符 (advertiserId)** **启用状态**
boolean isAdIdEnabled = true; // 默认启用广告 ID
callVCloudSettings_put(current_pkg_name + ".advertiserIdEnabled", String.valueOf(isAdIdEnabled), context);
JSONObject displayMetrics = new JSONObject();
displayMetrics.put("widthPixels", xPixels);
displayMetrics.put("heightPixels", yPixels);
displayMetrics.put("densityDpi", densityDpi);
displayMetrics.put("yDp", yDp);
callVCloudSettings_put("screen.device.displayMetrics", displayMetrics.toString(), context);
if (!ShellUtils.hasRootAccess()) {
LogFileUtil.writeLogToFile("ERROR", "ChangeDeviceInfoUtil", "Root access is required to execute system property changes");
}
// 设置机型, 直接设置属性
ShellUtils.execRootCmd("setprop ro.product.brand " + ro_product_brand);
ShellUtils.execRootCmd("setprop ro.product.model " + ro_product_model);
ShellUtils.execRootCmd("setprop ro.product.manufacturer " + ro_product_manufacturer);
ShellUtils.execRootCmd("setprop ro.product.device " + ro_product_device);
ShellUtils.execRootCmd("setprop ro.product.name " + ro_product_name);
ShellUtils.execRootCmd("setprop ro.build.version.incremental " + ro_build_version_incremental);
ShellUtils.execRootCmd("setprop ro.build.fingerprint " + ro_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.odm.build.fingerprint " + ro_odm_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.product.build.fingerprint " + ro_product_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.system.build.fingerprint " + ro_system_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.system_ext.build.fingerprint " + ro_system_ext_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.vendor.build.fingerprint " + ro_vendor_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.board.platform " + ro_build_platform);
// Native.setBootId(bootId);
// 修改drm id
ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id " + persist_sys_cloud_drm_id);
// 电量模拟需要大于1000
ShellUtils.execRootCmd("setprop persist.sys.cloud.battery.capacity " + persist_sys_cloud_battery_capacity);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_vendor " + persist_sys_cloud_gpu_gl_vendor);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer " + persist_sys_cloud_gpu_gl_renderer);
// 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_version " + persist_sys_cloud_gpu_gl_version);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_vendor " + persist_sys_cloud_gpu_egl_vendor);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version " + persist_sys_cloud_gpu_egl_version);
} catch (Throwable e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", e);
throw new RuntimeException("Error occurred in changeDeviceInfo", e);
}
afDeviceObject = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void callVCloudSettings_put(String key, String value, Context context) {
if (context == null) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Context cannot be null", null);
throw new IllegalArgumentException("Context cannot be null");
}
if (key == null || key.isEmpty()) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null);
throw new IllegalArgumentException("Key cannot be null or empty");
}
if (value == null) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Value cannot be null", null);
throw new IllegalArgumentException("Value cannot be null");
}
try {
// 获取类对象
Class<?> clazz = Class.forName("android.provider.VCloudSettings$Global");
Method putStringMethod = clazz.getDeclaredMethod("putString", ContentResolver.class, String.class, String.class);
putStringMethod.setAccessible(true);
// 调用方法
putStringMethod.invoke(null, context.getContentResolver(), key, value);
Log.d("Debug", "putString executed successfully.");
} catch (ClassNotFoundException e) {
logAndWrite(Log.WARN, "ChangeDeviceInfoUtil", "Class not found: android.provider.VCloudSettings$Global. This may not be supported on this device.", e);
} catch (NoSuchMethodException e) {
logAndWrite(Log.WARN, "ChangeDeviceInfoUtil", "Method not found: android.provider.VCloudSettings$Global.putString. This may not be supported on this", e);
} catch (InvocationTargetException e) {
Throwable cause = e.getTargetException();
if (cause instanceof SecurityException) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause);
} else {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause);
}
} catch (Exception e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Unexpected error during putString invocation", e);
}
}
public static void resetChangedDeviceInfo(String current_pkg_name, Context context) {
try {
Native.setBootId("00000000000000000000000000000000");
} catch (Exception e) {
logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Error occurred in reset", e);
}
if (!ShellUtils.hasRootAccess()) {
LogFileUtil.logAndWrite(Log.ERROR, "ChangeDeviceInfoUtil", "Root access is required to execute system property changes", null);
return;
}
ShellUtils.execRootCmd("cmd settings2 delete global global_android_id");
ShellUtils.execRootCmd("cmd settings2 delete global pm_list_features");
ShellUtils.execRootCmd("cmd settings2 delete global pm_list_libraries");
ShellUtils.execRootCmd("cmd settings2 delete global anticheck_pkgs");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_android_id");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_adb_enabled");
ShellUtils.execRootCmd("cmd settings2 delete global " + current_pkg_name + "_development_settings_enabled");
ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_vendor \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer \"\"");
// 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_version \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_vendor \"\"");
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version \"\"");
ShellUtils.execRootCmd("setprop ro.product.brand Vortex");
ShellUtils.execRootCmd("setprop ro.product.model HD65_Select");
ShellUtils.execRootCmd("setprop ro.product.manufacturer Vortex");
ShellUtils.execRootCmd("setprop ro.product.device HD65_Select");
ShellUtils.execRootCmd("setprop ro.product.name HD65_Select");
ShellUtils.execRootCmd("setprop ro.build.version.incremental 20240306");
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");
}
}

View File

@ -0,0 +1,63 @@
{
"cpuClockSpeed": "cpu_clock_speed_value",
"gaid": "gaid_value",
"userAgent": "User-Agent_value",
"osLang": "os_lang_value",
"osVer": "os_ver_value",
"tz": "tz_value",
"systemCountry": "system_country_value",
"simCountry": "sim_country_value",
"romFreeIn": "rom_free_in_value",
"resolution": "resolution_value",
"vendor": "vendor_value",
"batteryScale": "bat_scale_value",
"net": "net_value",
"dpi": "dpi_value",
"romFreeExt": "rom_free_ext_value",
"dpiF": "dpi_f_value",
"cpuCoreNum": "cpu_core_num_value",
"afDeviceObject": {
"advertiserId": "advertiserId_value",
"model": "model_value",
"brand": "brand_value",
"androidId": "android_id_value",
"xPixels": "x_px_value",
"yPixels": "y_px_value",
"densityDpi": "density_dpi_value",
"country": "country_value",
"batteryLevel": "batteryLevel_value",
"stackInfo": "stack_info_value",
"product": "product_value",
"network": "network_value",
"langCode": "lang_code_value",
"cpuAbi": "cpu_abi_value",
"yDp": "ydp_value",
"lang": "lang_value",
"roProductDetails": {
"brand": "ro_product_brand_value",
"model": "ro_product_model_value",
"manufacturer": "ro_product_manufacturer_value",
"device": "ro_product_device_value",
"name": "ro_product_name_value"
},
"roBuildDetails": {
"versionIncremental": "ro_build_version_incremental_value",
"fingerprint": "ro_build_fingerprint_value",
"productBuildFingerprint": "ro_product_build_fingerprint_value",
"systemBuildFingerprint": "ro_system_build_fingerprint_value",
"systemExtBuildFingerprint": "ro_system_ext_build_fingerprint_value",
"vendorBuildFingerprint": "ro_vendor_build_fingerprint_value"
},
"cloudProperties": {
"gpuProperties": {
"glVendor": "persist_sys_cloud_gpu_gl_vendor_value",
"glRenderer": "persist_sys_cloud_gpu_gl_renderer_value",
"glVersion": "persist_sys_cloud_gpu_gl_version_value",
"eglVendor": "persist_sys_cloud_gpu_egl_vendor_value",
"eglVersion": "persist_sys_cloud_gpu_egl_version_value"
},
"drmId": "persist_sys_cloud_drm_id_value",
"batteryCapacity": "persist_sys_cloud_battery_capacity_value"
}
}
}

View File

@ -0,0 +1,8 @@
package com.example.studyapp.device;
import org.json.JSONObject;
public interface LoadDeviceCallback{
void onLoadDeviceInfo(JSONObject bigoDevice, JSONObject afDevice);
void onLoadDeviceError(String error);
}

View File

@ -1,4 +1,4 @@
package com.example.retention.device; package com.example.studyapp.device;
public class Native { public class Native {
static { static {

View File

@ -0,0 +1,111 @@
package com.example.studyapp.job
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import androidx.core.app.JobIntentService
import com.example.studyapp.autoJS.AutoJsUtil
import com.example.studyapp.device.ChangeDeviceInfoUtil
import com.example.studyapp.device.LoadDeviceCallback
import com.example.studyapp.proxy.ClashUtil
import com.example.studyapp.task.TaskUtil
import com.example.studyapp.update.ChangeCallBack
import com.example.studyapp.update.UpdateUtil.changeDevice
import com.example.studyapp.utils.CountryCode
import com.example.studyapp.utils.IpUtil
import com.example.studyapp.utils.LogFileUtil
import org.json.JSONObject
import java.util.Locale
import java.util.UUID
class ScriptJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) {
execute(callback = object : JobCallback {
override fun onJobFailed() {
Handler(Looper.getMainLooper()).postDelayed({
StartJobService.onEvent(applicationContext)
}, 1000 * 20)
}
})
}
fun execute(callback: JobCallback) {
val taskId = UUID.randomUUID().toString()
runCatching {
ChangeDeviceInfoUtil.getAddDeviceInfo(
CountryCode.currentCountry.uppercase(Locale.getDefault()), CountryCode.DEVICE_TYPE,
object :LoadDeviceCallback {
override fun onLoadDeviceInfo(
bigoDevice: JSONObject?,
afDevice: JSONObject?
) {
// ClashUtil.startProxy(applicationContext)
ClashUtil.switchProxyWithPort(CountryCode.switchCountry())
// ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170")
val geoInfo: String = IpUtil.fetchGeoInfo()
val timeZone: String = IpUtil.getTimeZone(geoInfo)
val packageName = if (TextUtils.isEmpty(name)){
packageName
} else {
name
}
changeDevice(packageName, bigoDevice, afDevice, timeZone, object : ChangeCallBack {
override fun changeSuccess() {
runCatching {
AutoJsUtil.runAutojsScript(applicationContext)
TaskUtil.execSaveTask(
androidId,
taskId,
name,
IpUtil.fetchGeoInfo()
)
TaskUtil.uploadFile(applicationContext, androidId, name)
}.onFailure {
callback.onJobFailed()
}
}
override fun changeFailed() {
LogFileUtil.logAndWrite(
Log.ERROR,
TAG,
"Change device failed",
null
)
callback.onJobFailed()
}
})
}
override fun onLoadDeviceError(error: String?) {
callback.onJobFailed()
}
})
}.onFailure {
callback.onJobFailed()
}
}
companion object {
private const val TAG = "ScriptJobService"
private const val jobId = 102
private const val androidId = "FyZqWrStUvOpKlMn"
private var name = ""
fun onEvent(context: Context, packageName: String) {
name = packageName
enqueueWork(
context,
ScriptJobService::class.java, jobId, Intent(
context,
ScriptJobService::class.java
)
)
}
}
}

View File

@ -0,0 +1,153 @@
package com.example.studyapp.job
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.JobIntentService
import com.example.studyapp.autoJS.AutoJsUtil
import com.example.studyapp.device.ChangeDeviceInfoUtil
import com.example.studyapp.device.LoadDeviceCallback
import com.example.studyapp.job.ScriptJobService.Companion.onEvent
import com.example.studyapp.proxy.ClashUtil
import com.example.studyapp.update.ChangeCallBack
import com.example.studyapp.update.UpdateUtil.changeDevice
import com.example.studyapp.utils.CountryCode
import com.example.studyapp.utils.IpUtil
import com.example.studyapp.utils.LogFileUtil
import com.example.studyapp.utils.ShellUtils
import com.example.studyapp.utils.ShellUtils.exec
import org.json.JSONObject
import java.util.Locale
/**
* 监听任务
*/
interface JobCallback {
fun onJobFailed()
}
/**
* 这段代码定义了一个继承自 JobIntentService 的后台服务类 StartJobService整体功能如下
* 使用 onHandleWork 处理后台任务防止重复执行并在失败时延迟重启任务
* exec 方法中执行以下操作
* 通过 Root 命令授予应用特殊权限
* 获取并设置设备信息包括国家设备类型等
* 根据国家切换代理配置
* 获取 IP 地理信息和时区
* 修改设备信息若成功则运行 Auto.js 脚本若失败则回调失败方法
* 使用伴生对象管理任务状态与启动逻辑
*/
class StartJobService : JobIntentService() {
/**
* 重写 onHandleWork 方法处理后台任务
* 若任务已在运行则直接返回
* 设置任务状态为运行中
* 执行任务并在任务失败时通过主线程延迟 10 秒后重新触发事件
*/
override fun onHandleWork(intent: Intent) {
if (running) {
return
}
setRunning(true)
exec(
callback = object : JobCallback {
override fun onJobFailed() {
Handler(Looper.getMainLooper()).postDelayed({
onEvent(this@StartJobService)
}, 1000 * 10)
}
}
)
}
/**
* 执行一系列 Root 命令以授予应用特定权限
* 调用 ChangeDeviceInfoUtil 获取并设置设备信息
* 根据国家代码切换代理配置
* 获取 IP 地理信息和时区
* 调用 changeDevice 修改设备信息成功后运行 Auto.js 脚本
* 如果失败回调 onJobFailed 并结束任务
*/
protected fun exec(callback: JobCallback) {
try {
ShellUtils.execRootCmdAndGetResult("pm grant com.example.studyapp android.permission.INTERACT_ACROSS_USERS")
ShellUtils.execRootCmdAndGetResult("pm grant com.example.studyapp android.permission.WRITE_SECURE_SETTINGS")
ShellUtils.execRootCmdAndGetResult("pm setenforce 0")
ChangeDeviceInfoUtil.getAddDeviceInfo(
CountryCode.currentCountry.uppercase(Locale.getDefault()), CountryCode.DEVICE_TYPE,
object : LoadDeviceCallback {
override fun onLoadDeviceInfo(
bigoDevice: JSONObject?,
afDevice: JSONObject?
) {
// ClashUtil.startProxy(applicationContext)
// ClashUtil.switchProxyGroup("PROXY", "DIRECT", "http://127.0.0.1:6170")
ClashUtil.switchProxyWithPort(CountryCode.switchCountry())
// ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170")
val geoInfo: String = IpUtil.fetchGeoInfo()
val timeZone: String = IpUtil.getTimeZone(geoInfo)
changeDevice(packageName, bigoDevice, afDevice, timeZone, object : ChangeCallBack {
override fun changeSuccess() {
AutoJsUtil.runAutojsScript(applicationContext)
setRunning(false)
}
override fun changeFailed() {
LogFileUtil.logAndWrite(
Log.ERROR,
TAG,
"Change device failed",
null
)
callback.onJobFailed()
setRunning(false)
}
})
}
override fun onLoadDeviceError(error: String?) {
callback.onJobFailed()
setRunning(false)
}
})
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 定义伴生对象包含日志标签任务 ID 和运行状态标志
* 提供 onEvent 方法用于启动后台任务
* 提供 setRunning 方法更新任务运行状态
*/
companion object {
private const val TAG = "StartJobService"
private const val jobId = 101
private var running = false
/**
* onEvent 是一个函数接收 Context 参数
* 使用 enqueueWork 方法启动一个 StartJobService 类型的后台任务
* jobId 是任务的唯一标识符
* 创建了一个 Intent用于指定要启动的服务为 StartJobService
*/
fun onEvent(context: Context) {
enqueueWork(
context,
StartJobService::class.java, jobId, Intent(
context,
StartJobService::class.java
)
)
}
fun setRunning(runningV: Boolean) {
running = runningV
}
}
}

View File

@ -0,0 +1,12 @@
package com.example.studyapp.pad
data class DcInfo(
var area: String = "",
var dcCode: String = "",
var dcName: String = "",
var ossEndpoint: String = "",
var ossEndpointInternal: String = "",
var ossFileEndpoint: String = "",
var ossScreenshotEndpoint: String = ""
)

View File

@ -0,0 +1,13 @@
package com.example.studyapp.pad
import com.android.grape.pad.PageData
data class Pad(
var page: Int = 0,
var pageData: List<PageData> = listOf(),
var rows: Int = 0,
var size: Int = 0,
var total: Int = 0,
var totalPage: Int = 0
)

View File

@ -0,0 +1,8 @@
package com.example.studyapp.pad
data class PadTask(
var padCode: String = "",
var taskId: Int = 0,
var vmStatus: Int = 0
)

View File

@ -0,0 +1,17 @@
package com.android.grape.pad
import com.example.studyapp.pad.DcInfo
data class PageData(
var dataSize: Long = 0,
var dataSizeUsed: Long = 0,
var dcInfo: DcInfo = DcInfo(),
var deviceLevel: String = "",
var deviceStatus: Int = 0,
var imageId: String = "",
var online: Int = 0,
var padCode: String = "",
var padStatus: Int = 0,
var streamStatus: Int = 0
)

View File

@ -0,0 +1,12 @@
package com.example.studyapp.pad
data class TaskDetail(
var endTime: Long = 0,
var errorMsg: String = "",
var padCode: String = "",
var taskContent: Any? = Any(),
var taskId: Int = 0,
var taskResult: Any? = Any(),
var taskStatus: Int = 0
)

View File

@ -1,4 +1,4 @@
package com.example.retention.proxy; package com.example.studyapp.proxy;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -7,22 +7,28 @@ import android.content.IntentFilter;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import android.util.Log;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.example.retention.utils.LogFileUtil; import com.example.studyapp.utils.LogFileUtil;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import okhttp3.logging.HttpLoggingInterceptor;
/** /**
* @Time: 2025/6/9 11:13 * @Time: 2025/6/9 11:13

View File

@ -1,4 +1,4 @@
package com.example.retention.request; package com.example.studyapp.request;
// 这是发送到服务端的请求体JSON 格式 // 这是发送到服务端的请求体JSON 格式
public class ScriptResultRequest { public class ScriptResultRequest {

View File

@ -1,4 +1,4 @@
package com.example.retention.service; package com.example.studyapp.service;
import android.Manifest; import android.Manifest;
import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityService;
@ -6,17 +6,19 @@ import android.app.Notification;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.os.Build; import android.os.Build;
import android.os.IBinder;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.example.retention.MainActivity; import com.example.studyapp.MainActivity;
import com.example.retention.R; import com.example.studyapp.R;
public class MyAccessibilityService extends AccessibilityService { public class MyAccessibilityService extends AccessibilityService {

View File

@ -0,0 +1,23 @@
package com.example.studyapp.task;
public class AfInfo {
public String advertiserId;
public String model;
public String brand;
public String androidId;
public int xPixels;
public int yPixels;
public int densityDpi;
public String country;
public String batteryLevel;
public String stackInfo;
public String product;
public String network;
public String langCode;
public String cpuAbi;
public long yDp;
public String toString() {
return "advertiserId=" + advertiserId + ", model=" + model + ", brand=" + brand + ", androidId=" + androidId + ", xPixels=" + xPixels + ", yPixels=" + yPixels + ", densityDpi=" + densityDpi + ", country=" + country + ", batteryLevel=" + batteryLevel + ", stackInfo=" + stackInfo + ", product=" + product + ", network=" + network + ", langCode=" + langCode + ", cpuAbi=" + cpuAbi + ", yDp=" + yDp;
}
}

View File

@ -0,0 +1,48 @@
package com.example.studyapp.task;
import androidx.annotation.NonNull;
// 使用 JSON 库动态生成 JSON 请求体 (使用 Gson 示例)
public class BigoInfo {
public String cpuClockSpeed;
public String gaid;
public String userAgent;
public String osLang;
public String osVer;
public String tz;
public String systemCountry;
public String simCountry;
public long romFreeIn;
public String resolution;
public String vendor;
public int batteryScale;
public String net;
public long dpi;
public long romFreeExt;
public String dpiF;
public long cpuCoreNum;
@NonNull
@Override
public String toString() {
return "BigoInfo{" +
"cpuClockSpeed='" + cpuClockSpeed + '\'' +
", gaid='" + gaid + '\'' +
", userAgent='" + userAgent + '\'' +
", osLang='" + osLang + '\'' +
", osVer='" + osVer + '\'' +
", tz='" + tz + '\'' +
", systemCountry='" + systemCountry + '\'' +
", simCountry='" + simCountry + '\'' +
", romFreeIn=" + romFreeIn +
", resolution='" + resolution + '\'' +
", vendor='" + vendor + '\'' +
", batteryScale=" + batteryScale +
", net='" + net + '\'' +
", dpi=" + dpi +
", romFreeExt=" + romFreeExt +
", dpiF='" + dpiF + '\'' +
", cpuCoreNum=" + cpuCoreNum +
'}';
}
}

View File

@ -0,0 +1,56 @@
package com.example.studyapp.task;
import androidx.annotation.NonNull;
public class DeviceInfo {
public String lang;
public String roProductBrand;
public String roProductModel;
public String roProductManufacturer;
public String roProductDevice;
public String roProductName;
public String roBuildVersionIncremental;
public String roBuildFingerprint;
public String roOdmBuildFingerprint;
public String roProductBuildFingerprint;
public String roSystemBuildFingerprint;
public String roSystemExtBuildFingerprint;
public String roVendorBuildFingerprint;
public String roBuildPlatform;
public String persistSysCloudDrmId;
public int persistSysCloudBatteryCapacity;
public String persistSysCloudGpuGlVendor;
public String persistSysCloudGpuGlRenderer;
public String persistSysCloudGpuGlVersion;
public String persistSysCloudGpuEglVendor;
public String persistSysCloudGpuEglVersion;
@NonNull
@Override
public String toString() {
return "DeviceInfo{" +
"lang='" + lang + '\'' +
", roProductBrand='" + roProductBrand + '\'' +
", roProductModel='" + roProductModel + '\'' +
", roProductManufacturer='" + roProductManufacturer + '\'' +
", roProductDevice='" + roProductDevice + '\'' +
", roProductName='" + roProductName + '\'' +
", roBuildVersionIncremental='" + roBuildVersionIncremental + '\'' +
", roBuildFingerprint='" + roBuildFingerprint + '\'' +
", roOdmBuildFingerprint='" + roOdmBuildFingerprint + '\'' +
", roProductBuildFingerprint='" + roProductBuildFingerprint + '\'' +
", roSystemBuildFingerprint='" + roSystemBuildFingerprint + '\'' +
", roSystemExtBuildFingerprint='" + roSystemExtBuildFingerprint + '\'' +
", roVendorBuildFingerprint='" + roVendorBuildFingerprint + '\'' +
", roBuildPlatform='" + roBuildPlatform + '\'' +
", persistSysCloudDrmId='" + persistSysCloudDrmId + '\'' +
", persistSysCloudBatteryCapacity=" + persistSysCloudBatteryCapacity +
", persistSysCloudGpuGlVendor='" + persistSysCloudGpuGlVendor + '\'' +
", persistSysCloudGpuGlRenderer='" + persistSysCloudGpuGlRenderer + '\'' +
", persistSysCloudGpuGlVersion='" + persistSysCloudGpuGlVersion + '\'' +
", persistSysCloudGpuEglVendor='" + persistSysCloudGpuEglVendor + '\'' +
", persistSysCloudGpuEglVersion='" + persistSysCloudGpuEglVersion + '\'' +
'}';
}
}

View File

@ -1,9 +1,13 @@
package com.example.retention.task; package com.example.studyapp.task;
import android.content.Context; import android.content.Context;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.example.retention.utils.LogFileUtil;
import com.example.retention.utils.ShellUtils; import com.example.studyapp.utils.FileUtils;
import com.example.studyapp.utils.LogFileUtil;
import com.example.studyapp.utils.ShellUtils;
import com.example.studyapp.utils.ZipUtils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
@ -17,6 +21,7 @@ import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.HttpUrl.Builder;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -50,7 +55,7 @@ public class TaskUtil {
.readTimeout(30, TimeUnit.SECONDS) // 读取超时 .readTimeout(30, TimeUnit.SECONDS) // 读取超时
.build(); .build();
public static void postDeviceInfo(String androidId, String taskId,String packageName) { public static void postDeviceInfo(String androidId, String taskId,String packageName, String ipInfo) {
Log.i("TaskUtil", "postDeviceInfo called with androidId: " + androidId); Log.i("TaskUtil", "postDeviceInfo called with androidId: " + androidId);
if (okHttpClient == null) { if (okHttpClient == null) {
@ -58,33 +63,28 @@ public class TaskUtil {
throw new IllegalStateException("HttpClient is not initialized"); throw new IllegalStateException("HttpClient is not initialized");
} }
if (BASE_URL == null || BASE_URL.isEmpty()) { Log.d("TaskUtil", "Creating payload for the request...");
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "BASE_URL is not initialized", null);
throw new IllegalStateException("BASE_URL is not initialized");
}
Log.d("TaskUtil", "Creating payload for the request...");
Payload payload = new Payload(); Payload payload = new Payload();
payload.bigoDeviceObject = bigoDevice; payload.bigoDeviceObject = bigoDevice;
payload.afDeviceObject = afDevice; payload.afDeviceObject = afDevice;
payload.other = deviceInfo; payload.other = deviceInfo;
Gson gson = new GsonBuilder().serializeNulls().create(); Gson gson = new GsonBuilder().serializeNulls().create();
String jsonRequestBody = gson.toJson(payload); String jsonRequestBody = gson.toJson(payload);
Log.d("TaskUtil", "Request payload: " + jsonRequestBody); Log.d("TaskUtil", "Request payload: " + jsonRequestBody);
if (packageName == null){ Builder urlBuilder = HttpUrl.parse(BASE_URL)
packageName = "";
}
HttpUrl url = HttpUrl.parse(BASE_URL)
.newBuilder() .newBuilder()
.addPathSegment("device_info_upload") .addPathSegment("device_info_upload")
.addQueryParameter("id", androidId) .addQueryParameter("id", androidId)
.addQueryParameter("taskId", taskId) .addQueryParameter("taskId", taskId)
.addQueryParameter("packageName", packageName) .addQueryParameter("deviceIp",ipInfo);
.build();
if (!TextUtils.isEmpty(packageName)){
urlBuilder.addQueryParameter("packageName", packageName);
}
HttpUrl url = urlBuilder.build();
Log.d("TaskUtil", "Request URL: " + url.toString()); Log.d("TaskUtil", "Request URL: " + url.toString());
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonRequestBody); RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonRequestBody);
@ -139,7 +139,6 @@ public class TaskUtil {
Log.d("TaskUtil", "Built HTTP request for device info download"); Log.d("TaskUtil", "Built HTTP request for device info download");
try (Response response = okHttpClient.newCall(request).execute()) { try (Response response = okHttpClient.newCall(request).execute()) {
LogFileUtil.logAndWrite(android.util.Log.DEBUG, "TaskUtil", "Response : " + response, null);
// 检查响应是否成功 // 检查响应是否成功
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
String errorMessage = "Unexpected response: Code=" + response.code() + String errorMessage = "Unexpected response: Code=" + response.code() +
@ -178,13 +177,13 @@ public class TaskUtil {
} }
public static void infoUpload(Context context, String androidId, String packAge) throws IOException { public static void uploadFile(Context context, String androidId, String packAge) throws IOException {
Log.i("TaskUtil", "infoUpload called with androidId: " + androidId + ", package: " + packAge); Log.i("TaskUtil", "infoUpload called with androidId: " + androidId + ", package: " + packAge);
if (packAge == null || packAge.isEmpty()) { if (packAge == null || packAge.isEmpty()) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Package name is null or empty", null); LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Package name is null or empty", null);
throw new IllegalArgumentException("Package name cannot be null or empty"); return;
} }
if (context == null) { if (context == null) {
@ -214,11 +213,12 @@ public class TaskUtil {
if (zipFile.exists()) { if (zipFile.exists()) {
delFileSh(zipFile.getAbsolutePath()); delFileSh(zipFile.getAbsolutePath());
} }
File copiedAPKFile = new File(context.getCacheDir(), packAge+"_upload.apk"); File copiedAPKFile = new File(context.getCacheDir(), packAge);
if (copiedAPKFile.exists()) { if (copiedAPKFile.exists()) {
delFileSh(copiedAPKFile.getAbsolutePath()); delFileSh(copiedAPKFile.getAbsolutePath());
} }
copyFolderSh(apkSourceFile, copiedAPKFile.getAbsolutePath()); String parentPath = FileUtils.getParentDirectory(apkSourceFile);
copyFolderSh(parentPath, copiedAPKFile.getAbsolutePath());
// boolean success = clearUpFileInDst(copiedDir); // boolean success = clearUpFileInDst(copiedDir);
// if (success) { // if (success) {
// // 压缩APK文件 // // 压缩APK文件
@ -226,20 +226,29 @@ public class TaskUtil {
// } // }
// 压缩APK文件 // 压缩APK文件
compressToZip(copiedAPKFile, zipFile); // compressToZip(copiedAPKFile, zipFile);
try {
ZipUtils.zipDirectory(copiedAPKFile.getAbsolutePath(), zipFile.getAbsolutePath());
}catch (Exception e){
zipFile.delete();
e.printStackTrace();
}
//uninstall
String uninstall = "pm uninstall "+packAge;
String chmodResult = ShellUtils.execRootCmdAndGetResult(uninstall);
Log.e("TAG", "infoUpload compress finish: ");
// 上传压缩文件 // 上传压缩文件
if (!zipFile.exists()) { if (!zipFile.exists()) {
Log.w("TaskUtil", "Upload file does not exist: " + outputZipPath); Log.w("TaskUtil", "Upload file does not exist: " + outputZipPath);
return; return;
} }
String safeNewPath = zipFile.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
String chmod = "chmod 777 \"" + safeNewPath + "\"";
uploadFile(zipFile); uploadFile(zipFile);
//uninstall
String uninstall = "pm uninstall "+packAge;
String chmodResult = ShellUtils.execRootCmdAndGetResult(uninstall);
// 清理临时文件 // 清理临时文件
delFileSh(copiedAPKFile.getAbsolutePath()); delFileSh(copiedAPKFile.getAbsolutePath());
ShellUtils.execRootCmdAndGetResult(chmod);
delFileSh(zipFile.getAbsolutePath()); delFileSh(zipFile.getAbsolutePath());
} }
@ -272,6 +281,7 @@ public class TaskUtil {
String result = ShellUtils.execRootCmdAndGetResult(cmd); String result = ShellUtils.execRootCmdAndGetResult(cmd);
String chmod = "chmod 777 \"" + safeNewPath + "\""; String chmod = "chmod 777 \"" + safeNewPath + "\"";
String chmodResult = ShellUtils.execRootCmdAndGetResult(chmod); String chmodResult = ShellUtils.execRootCmdAndGetResult(chmod);
recursiveChmod777(dst);
// if (!TextUtils.isEmpty(chmodResult)) { // if (!TextUtils.isEmpty(chmodResult)) {
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "chmodResult. Result: " + chmodResult, null); LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "chmodResult. Result: " + chmodResult, null);
// return false; // return false;
@ -289,12 +299,37 @@ public class TaskUtil {
} }
} }
public static void recursiveChmod777(File dir) {
if (dir == null || !dir.exists()) {
Log.e("Chmod", "目录不存在或为null");
return;
}
// 先修改当前目录权限
String currentPath = dir.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
String chmodCmd = "chmod 777 \"" + currentPath + "\"";
ShellUtils.execRootCmdAndGetResult(chmodCmd);
// 递归处理子项
File[] children = dir.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory()) {
recursiveChmod777(child); // 递归处理子目录
} else {
String filePath = child.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
chmodCmd = "chmod 777 \"" + filePath + "\"";
ShellUtils.execRootCmdAndGetResult(chmodCmd);
}
}
}
}
/** /**
*在给定的目标目录中清除特定的文件和目录 *在给定的目标目录中清除特定的文件和目录
*子目录未命名为缓存并且删除了大于3 MB的文件 *子目录未命名为缓存并且删除了大于3 MB的文件
*记录保留的文件和目录的路径 *记录保留的文件和目录的路径
* *
* @param DST将处理其文件和子目录的目标目录
* @return true如果目的目录存在并且已成功处理则为false否则 * @return true如果目的目录存在并且已成功处理则为false否则
*/ */
private static boolean clearUpFileInDst(File dst) { private static boolean clearUpFileInDst(File dst) {
@ -612,7 +647,28 @@ public class TaskUtil {
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
Log.d("TaskUtil", "Response is successful. Preparing to save file."); // 记录成功响应 Log.d("TaskUtil", "Response is successful. Preparing to save file."); // 记录成功响应
return saveDownloadedFile(response, filesLocationDir, fileName); // 检查目录是否存在
if (!filesLocationDir.exists()) {
boolean dirCreated = filesLocationDir.mkdirs();
Log.d("TaskUtil", "Directory created: " + filesLocationDir.getAbsolutePath() + " - " + dirCreated);
}
File saveFile = new File(filesLocationDir, fileName);
Log.d("TaskUtil", "Target file path: " + saveFile.getAbsolutePath());
try (InputStream is = response.body().byteStream();
OutputStream os = new BufferedOutputStream(new FileOutputStream(saveFile))) {
Log.d("TaskUtil", "Starting to write file...");
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
Log.i("TaskUtil", "File saved successfully to: " + saveFile.getAbsolutePath());
}
return saveFile;
} else { } else {
Log.w("TaskUtil", "Download failed. HTTP code: " + response.code() + ", Message: " + response.message()); Log.w("TaskUtil", "Download failed. HTTP code: " + response.code() + ", Message: " + response.message());
@ -635,55 +691,6 @@ public class TaskUtil {
} }
} }
private static File saveDownloadedFile(Response response, File filesLocationDir, String fileName) throws IOException {
// 1. 更严格的目录检查和处理
if (!filesLocationDir.exists()) {
if (!filesLocationDir.mkdirs()) {
throw new IOException("Failed to create directory: " + filesLocationDir.getAbsolutePath());
}
Log.d("TaskUtil", "Directory created: " + filesLocationDir.getAbsolutePath());
}
// 2. 检查目录是否可写
if (!filesLocationDir.canWrite()) {
throw new IOException("Directory not writable: " + filesLocationDir.getAbsolutePath());
}
// 3. 处理文件名冲突
File saveFile = new File(filesLocationDir, fileName);
if (saveFile.exists()) {
saveFile.delete();
}
// 4. 更完善的写入流程
File tempFile = new File(filesLocationDir, fileName + ".tmp");
try (InputStream is = response.body().byteStream();
OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
// 5. 原子性重命名确保文件完整
if (!tempFile.renameTo(saveFile)) {
throw new IOException("Failed to rename temp file to: " + saveFile.getAbsolutePath());
}
Log.i("TaskUtil", "File saved successfully to: " + saveFile.getAbsolutePath());
return saveFile;
} catch (IOException e) {
// 6. 清理不完整文件
if (tempFile.exists()) {
boolean deleted = tempFile.delete();
Log.w("TaskUtil", "Deleted incomplete file: " + deleted);
}
throw e;
}
}
private static void validate() { private static void validate() {
if (okHttpClient == null) { if (okHttpClient == null) {
throw new IllegalStateException("HttpClient is not initialized"); throw new IllegalStateException("HttpClient is not initialized");
@ -760,9 +767,10 @@ public class TaskUtil {
return getDeviceInfoSync(androidId,taskId); return getDeviceInfoSync(androidId,taskId);
} }
public static void execSaveTask(Context context, String androidId, String taskId,String packName) { public static void execSaveTask(String androidId, String taskId,String packName, String ipInfo) {
if (context == null) { if (packName == null || packName.isEmpty()) {
throw new IllegalArgumentException("Context or Package name cannot be null or empty"); LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Package name is null or empty", null);
return;
} }
if (androidId == null || androidId.isEmpty()) { if (androidId == null || androidId.isEmpty()) {
@ -771,7 +779,7 @@ public class TaskUtil {
} }
try { try {
postDeviceInfo(androidId, taskId,packName); postDeviceInfo(androidId, taskId,packName,ipInfo);
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error in execReloginTask: " + e.getMessage()); System.err.println("Error in execReloginTask: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
@ -801,3 +809,4 @@ class Payload {
AfInfo afDeviceObject; AfInfo afDeviceObject;
DeviceInfo other; DeviceInfo other;
} }

View File

@ -0,0 +1,76 @@
package com.example.studyapp.update
import android.util.Log
import com.example.studyapp.pad.Pad
import com.example.studyapp.pad.PadTask
import com.example.studyapp.pad.TaskDetail
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import org.json.JSONArray
import org.json.JSONObject
import kotlin.apply
import kotlin.collections.isNotEmpty
import kotlin.to
import kotlin.toString
object Api {
private const val TAG = "Api"
const val UPDATE_PAD: String = "/openapi/open/pad/updatePadProperties"
const val PAD_DETAIL: String = "/openapi/open/pad/padDetails"
const val PAD_TASK: String = "/task-center/open/task/padTaskDetail"
fun padDetail(): ApiResponse<Pad>{
val param = Gson().toJson(
mapOf(
"page" to 1,
"rows" to 10,
"padCode" to listOf("ACP250702PWJCTLF")
)
)
val (response, code) = HttpUtils.postSync(PAD_DETAIL, param)
if (code == 200) {
Log.d("padDetail", response.toString())
var apiResponse: ApiResponse<Pad> = Gson().fromJson(response, object : TypeToken<ApiResponse<Pad>>() {}.type)
return apiResponse
} else {
Log.d("padDetail", "error: $code")
return ApiResponse(code, "error", 0, null)
}
}
fun updatePad(params: String): ApiResponseList<PadTask>{
val (response, code) = HttpUtils.postSync(UPDATE_PAD, params)
if (code == 200) {
Log.d("updatePad", response.toString())
var apiResponse: ApiResponseList<PadTask> = Gson().fromJson(response, object : TypeToken<ApiResponseList<PadTask>>() {}.type)
return apiResponse
} else {
Log.d("updatePad", "error: $code")
return ApiResponseList(code, "error", 0, emptyList())
}
}
fun padTaskDetail(taskId: Int): Int{
val jsonString = JSONObject().apply {
put("taskIds", JSONArray().apply {
put(taskId)
})
}.toString()
Log.d(TAG, "padTaskDetail: $jsonString")
val (response, code) = HttpUtils.postSync(PAD_TASK, jsonString)
if (code == 200) {
Log.d("padTaskDetail", response.toString())
var apiResponse: ApiResponseList<TaskDetail> = Gson().fromJson(response, object : TypeToken<ApiResponseList<TaskDetail>>() {}.type)
val taskList = apiResponse.data
if (apiResponse.isSuccess() && taskList!= null && taskList.isNotEmpty()){
val task = taskList[0]
return task.taskStatus
}
return -1
}else {
Log.d("padTaskDetail", "error: $code")
return -1
}
}
}

View File

@ -0,0 +1,23 @@
package com.example.studyapp.update
data class ApiResponse<T>(
val code: Int,
val message: String,
val ts: Long,
var data: T? = null
){
fun isSuccess(): Boolean {
return code == 200
}
}
data class ApiResponseList<T>(
val code: Int,
val message: String,
val ts: Long,
var data: List<T>? = null
){
fun isSuccess(): Boolean {
return code == 200
}
}

View File

@ -0,0 +1,30 @@
package com.example.studyapp.update;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class ArmCloudSignatureV2 {
public static final String ALGORITHM = "HmacSHA256";
public static final String SECRET_KEY = "gz8f1u0t63byzdu6ozbx8r5qs3e5lipt";
public static final String SECRET_ID = "3yc8c8bg1dym0zaiwjh867al";
public static String calculateSignature(String timestamp, String path, String body) throws Exception {
String stringToSign = timestamp + path + (body != null ? body : "");
Mac hmacSha256 = Mac.getInstance(ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_ID.getBytes(StandardCharsets.UTF_8), ALGORITHM);
hmacSha256.init(secretKeySpec);
byte[] hash = hmacSha256.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}

View File

@ -0,0 +1,6 @@
package com.example.studyapp.update
interface ChangeCallBack {
fun changeSuccess()
fun changeFailed()
}

View File

@ -0,0 +1,258 @@
package com.example.studyapp.update
import android.util.Log
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
object HttpUtils {
const val HOST: String = "https://openapi-hk.armcloud.net"
private val client: OkHttpClient by lazy {
val trustAllCerts: Array<TrustManager> = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
override fun checkServerTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
override fun getAcceptedIssuers(): Array<X509Certificate?>? {
return arrayOf<X509Certificate?>()
}
}
)
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(HostnameVerifier { _, _ -> true })
.build()
}
// JSON媒体类型
private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull()
// 回调接口
interface HttpCallback {
fun onSuccess(response: String, code: Int)
fun onFailure(error: String, code: Int?)
}
/**
* GET 请求异步
*
* @param url 请求URL
* @param params 查询参数可选
* @param headers 请求头可选
* @param callback 回调接口
*/
fun getAsync(
url: String,
params: Map<String, String>? = null,
callback: HttpCallback
) {
val request = buildGetRequest(url, params)
executeRequestAsync(request, callback)
}
/**
* POST 请求异步
*
* @param url 请求URL
* @param body 请求体JSON格式
* @param headers 请求头可选
* @param callback 回调接口
*/
fun postAsync(
url: String,
body: String? = null,
callback: HttpCallback
) {
val request = buildPostRequest(url, body)
executeRequestAsync(request, callback)
}
/**
* GET 请求同步
*
* @param url 请求URL
* @param params 查询参数可选
* @param headers 请求头可选
* @return Pair<响应内容, 状态码>
*/
fun getSync(
url: String,
params: Map<String, String>? = null,
): Pair<String?, Int> {
val request = buildGetRequest(url, params)
return executeRequestSync(request)
}
/**
* POST 请求同步
*
* @param url 请求URL
* @param body 请求体JSON格式
* @param headers 请求头可选
* @return Pair<响应内容, 状态码>
*/
fun postSync(
url: String,
body: String? = null
): Pair<String?, Int> {
val request = buildPostRequest(url, body)
return executeRequestSync(request)
}
/**
* 取消所有请求
*/
fun cancelAllRequests() {
client.dispatcher.cancelAll()
}
// 内部方法 --------------------------------
private fun buildGetRequest(
path: String ,
params: Map<String, String>?,
): Request {
val url = "$HOST$path"
val httpUrlBuilder = url.toHttpUrlOrNull()?.newBuilder()
?: throw IllegalArgumentException("Invalid URL: $url")
var param = ""
params?.forEach { (key, value) ->
httpUrlBuilder.addQueryParameter(key, value)
param += "&${key}=${value}"
}
if (param.isNotEmpty()){
param = param.substring(1)
}
val requestBuilder = Request.Builder().url(httpUrlBuilder.build())
val authver = "2.0"
val timestamp = System.currentTimeMillis()
val sign =
ArmCloudSignatureV2.calculateSignature(timestamp.toString(), path, param)
addHeaders(
requestBuilder,
mapOf(
"authver" to authver,
"x-ak" to ArmCloudSignatureV2.SECRET_KEY,
"x-timestamp" to timestamp.toString(),
"x-sign" to sign
)
)
return requestBuilder.build()
}
private fun buildPostRequest(
path: String,
body: String? = null
): Request {
val url = "$HOST$path"
val bodyString = body?:""
val requestBuilder = Request.Builder()
.url(url)
.post(bodyString.toRequestBody(JSON_MEDIA_TYPE))
val authver = "2.0"
val timestamp = System.currentTimeMillis()
val sign =
ArmCloudSignatureV2.calculateSignature(timestamp.toString(), path, body)
addHeaders(
requestBuilder,
mapOf(
"authver" to authver,
"x-ak" to ArmCloudSignatureV2.SECRET_KEY,
"x-timestamp" to timestamp.toString(),
"x-sign" to sign
)
)
return requestBuilder.build()
}
private fun addHeaders(
builder: Request.Builder,
headers: Map<String, String>
) {
headers.forEach { (key, value) ->
builder.addHeader(key, value)
}
}
private fun buildFormBody(formData: Map<String, String>): RequestBody {
val formBodyBuilder = FormBody.Builder()
formData.forEach { (key, value) ->
formBodyBuilder.add(key, value)
}
return formBodyBuilder.build()
}
private fun executeRequestAsync(
request: Request,
callback: HttpCallback
) {
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val responseBody = response.body?.string()
val code = response.code
if (response.isSuccessful && responseBody != null) {
callback.onSuccess(responseBody, code)
} else {
callback.onFailure(
responseBody ?: "Empty response",
code
)
}
}
override fun onFailure(call: Call, e: IOException) {
callback.onFailure(e.message ?: "Unknown network error", null)
}
})
}
private fun executeRequestSync(request: Request): Pair<String?, Int> {
return try {
val response = client.newCall(request).execute()
val responseBody = response.peekBody(Long.MAX_VALUE).string()
val code = response.code
Log.d("TAG", "executeRequestSync: $responseBody")
if (response.isSuccessful && responseBody.isNotEmpty()) {
Pair(responseBody, code)
} else {
Pair(null, -1)
}
} catch (e: IOException) {
Pair(null, -1) // 网络错误状态码
}
}
}

View File

@ -0,0 +1,318 @@
package com.example.studyapp.update
import android.util.Log
import com.example.studyapp.task.AfInfo
import com.example.studyapp.task.BigoInfo
import com.example.studyapp.task.DeviceInfo
import com.example.studyapp.task.TaskUtil
import com.example.studyapp.utils.ShellUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
object UpdateUtil {
val scope = CoroutineScope(Dispatchers.IO)
fun changeDevice(
recordPackageName: String,
bigoDeviceObject: JSONObject?,
afDeviceObject: JSONObject?,
time: String,
callback: ChangeCallBack
) {
try {
val padCode = ShellUtils.execRootCmdAndGetResult("getprop ro.boot.pad_code")
Log.d("TAG", "changeDevice: $padCode")
val jsonObject = JSONObject().apply {
put("padCodes", JSONArray().apply {
put(padCode)
})
}
var timeZone: String = ""
bigoDeviceObject?.apply {
val cpuClockSpeed: String = bigoDeviceObject.optString("cpu_clock_speed")
val gaid: String = bigoDeviceObject.optString("gaid")
val userAgent: String = bigoDeviceObject.optString("User-Agent")
val osLang: String = bigoDeviceObject.optString("os_lang")
val osVer: String = bigoDeviceObject.optString("os_ver")
timeZone = bigoDeviceObject.optString("tz")
val systemCountry: String = bigoDeviceObject.optString("system_country")
val simCountry: String = bigoDeviceObject.optString("sim_country")
val romFreeIn: Long = bigoDeviceObject.optLong("rom_free_in")
val resolution: String = bigoDeviceObject.optString("resolution")
val vendor: String = bigoDeviceObject.optString("vendor")
val batteryScale: Int = bigoDeviceObject.optInt("bat_scale")
val net: String = bigoDeviceObject.optString("net")
val dpi: Int = bigoDeviceObject.optInt("dpi")
val romFreeExt: Long = bigoDeviceObject.optLong("rom_free_ext")
val dpiF: String = bigoDeviceObject.optString("dpi_f")
val cpuCoreNum: Int = bigoDeviceObject.optInt("cpu_core_num")
val bigoDevice = BigoInfo()
bigoDevice.cpuClockSpeed = cpuClockSpeed
bigoDevice.gaid = gaid
bigoDevice.userAgent = userAgent
bigoDevice.osLang = osLang
bigoDevice.osVer = osVer
bigoDevice.tz = timeZone
bigoDevice.systemCountry = systemCountry
bigoDevice.simCountry = simCountry
bigoDevice.romFreeIn = romFreeIn
bigoDevice.resolution = resolution
bigoDevice.vendor = vendor
bigoDevice.batteryScale = batteryScale
bigoDevice.net = net
bigoDevice.dpi = dpi.toLong()
bigoDevice.romFreeExt = romFreeExt
bigoDevice.dpiF = dpiF
bigoDevice.cpuCoreNum = cpuCoreNum.toLong()
TaskUtil.setBigoDevice(bigoDevice)
}
afDeviceObject?.apply {
val advertiserId = afDeviceObject.optString(".advertiserId")
val model = afDeviceObject.optString(".model")
val brand = afDeviceObject.optString(".brand")
val androidId = afDeviceObject.optString(".android_id")
val xPixels = afDeviceObject.optInt(".deviceData.dim.x_px")
val yPixels = afDeviceObject.optInt(".deviceData.dim.y_px")
val densityDpi = afDeviceObject.optInt(".deviceData.dim.d_dpi")
val country = afDeviceObject.optString(".country")
val batteryLevel = afDeviceObject.optString(".batteryLevel")
val stackInfo = Thread.currentThread().getStackTrace()[2].toString()
val product = afDeviceObject.optString(".product")
val network = afDeviceObject.optString(".network")
val langCode = afDeviceObject.optString(".lang_code")
val cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi")
val yDp = afDeviceObject.optInt(".deviceData.dim.ydp")
val lang = afDeviceObject.optString(".lang")
val ro_product_brand = afDeviceObject.optString("ro.product.brand", "")
val ro_product_model = afDeviceObject.optString("ro.product.model", "")
val ro_product_manufacturer =
afDeviceObject.optString("ro.product.manufacturer", "")
val ro_product_device = afDeviceObject.optString("ro.product.device", "")
val ro_product_name = afDeviceObject.optString("ro.product.name", "")
val ro_build_version_incremental =
afDeviceObject.optString("ro.build.version.incremental", "")
val ro_build_fingerprint = afDeviceObject.optString("ro.build.fingerprint", "")
val ro_odm_build_fingerprint =
afDeviceObject.optString("ro.odm.build.fingerprint", "")
val ro_product_build_fingerprint =
afDeviceObject.optString("ro.product.build.fingerprint", "")
val ro_system_build_fingerprint =
afDeviceObject.optString("ro.system.build.fingerprint", "")
val ro_system_ext_build_fingerprint =
afDeviceObject.optString("ro.system_ext.build.fingerprint", "")
val ro_vendor_build_fingerprint =
afDeviceObject.optString("ro.vendor.build.fingerprint", "")
val ro_build_platform = afDeviceObject.optString("ro.board.platform", "")
val persist_sys_cloud_drm_id =
afDeviceObject.optString("persist.sys.cloud.drm.id", "")
val persist_sys_cloud_battery_capacity =
afDeviceObject.optInt("persist.sys.cloud.battery.capacity", -1)
val persist_sys_cloud_gpu_gl_vendor =
afDeviceObject.optString("persist.sys.cloud.gpu.gl_vendor", "")
val persist_sys_cloud_gpu_gl_renderer =
afDeviceObject.optString("persist.sys.cloud.gpu.gl_renderer", "")
val persist_sys_cloud_gpu_gl_version =
afDeviceObject.optString("persist.sys.cloud.gpu.gl_version", "")
val persist_sys_cloud_gpu_egl_vendor =
afDeviceObject.optString("persist.sys.cloud.gpu.egl_vendor", "")
val persist_sys_cloud_gpu_egl_version =
afDeviceObject.optString("persist.sys.cloud.gpu.egl_version", "")
val global_android_id = afDeviceObject.optString(".android_id", "")
val anticheck_pkgs = afDeviceObject.optString(".anticheck_pkgs", "")
val pm_list_features = afDeviceObject.optString(".pm_list_features", "")
val pm_list_libraries = afDeviceObject.optString(".pm_list_libraries", "")
val system_http_agent = afDeviceObject.optString("system.http.agent", "")
val webkit_http_agent = afDeviceObject.optString("webkit.http.agent", "")
val com_fk_tools_pkgInfo = afDeviceObject.optString(".pkg_info", "")
val appsflyerKey = afDeviceObject.optString(".appsflyerKey", "")
val appUserId = afDeviceObject.optString(".appUserId", "")
val disk = afDeviceObject.optString(".disk", "")
val operator = afDeviceObject.optString(".operator", "")
val cell_mcc = afDeviceObject.optString(".cell.mcc", "")
val cell_mnc = afDeviceObject.optString(".cell.mnc", "")
val date1 = afDeviceObject.optString(".date1", "")
val date2 = afDeviceObject.optString(".date2", "")
val bootId = afDeviceObject.optString("BootId", "")
if (time.isNotEmpty()){
timeZone = time
}
val afDevice = AfInfo()
afDevice.advertiserId = advertiserId
afDevice.model = model
afDevice.brand = brand
afDevice.androidId = androidId
afDevice.xPixels = xPixels
afDevice.yPixels = yPixels
afDevice.densityDpi = densityDpi
afDevice.country = country
afDevice.batteryLevel = batteryLevel
afDevice.stackInfo = stackInfo
afDevice.product = product
afDevice.network = network
afDevice.langCode = langCode
afDevice.cpuAbi = cpuAbi
afDevice.yDp = yDp.toLong()
TaskUtil.setAfDevice(afDevice)
val deviceInfo = DeviceInfo()
deviceInfo.lang = lang
deviceInfo.roProductBrand = ro_product_brand
deviceInfo.roProductModel = ro_product_model
deviceInfo.roProductManufacturer = ro_product_manufacturer
deviceInfo.roProductDevice = ro_product_device
deviceInfo.roProductName = ro_product_name
deviceInfo.roBuildVersionIncremental = ro_build_version_incremental
deviceInfo.roBuildFingerprint = ro_build_fingerprint
deviceInfo.roOdmBuildFingerprint = ro_odm_build_fingerprint
deviceInfo.roProductBuildFingerprint = ro_product_build_fingerprint
deviceInfo.roSystemBuildFingerprint = ro_system_build_fingerprint
deviceInfo.roSystemExtBuildFingerprint = ro_system_ext_build_fingerprint
deviceInfo.roVendorBuildFingerprint = ro_vendor_build_fingerprint
deviceInfo.roBuildPlatform = ro_build_platform
deviceInfo.persistSysCloudDrmId = persist_sys_cloud_drm_id
deviceInfo.persistSysCloudBatteryCapacity = persist_sys_cloud_battery_capacity
deviceInfo.persistSysCloudGpuGlVendor = persist_sys_cloud_gpu_gl_vendor
deviceInfo.persistSysCloudGpuGlRenderer = persist_sys_cloud_gpu_gl_renderer
deviceInfo.persistSysCloudGpuGlVersion = persist_sys_cloud_gpu_gl_version
deviceInfo.persistSysCloudGpuEglVendor = persist_sys_cloud_gpu_egl_vendor
deviceInfo.persistSysCloudGpuEglVersion = persist_sys_cloud_gpu_egl_version
TaskUtil.setDeviceInfo(deviceInfo)
jsonObject.apply {
put("modemPropertiesList", JSONArray().apply {
put(JSONObject().apply {
put("propertiesName", "MCCMNC")
put("propertiesValue", "${cell_mcc},${cell_mnc}")
})
})
put("systemPropertiesList", JSONArray().apply {
put(JSONObject().apply {
put("propertiesName", "ro.product.manufacturer")
put("propertiesValue", ro_product_manufacturer)
})
put(JSONObject().apply {
put("propertiesName", "ro.product.brand")
put("propertiesValue", ro_product_brand)
})
put(JSONObject().apply {
put("propertiesName", "ro.product.model")
put("propertiesValue", ro_product_model)
})
// put(JSONObject().apply {
// put("propertiesName", "ro.build.display.id")
// put("propertiesValue", )
// })
put(JSONObject().apply {
put("propertiesName", "ro.product.name")
put("propertiesValue", ro_product_name)
})
put(JSONObject().apply {
put("propertiesName", "ro.product.device")
put("propertiesValue", ro_product_device)
})
// put(JSONObject().apply {
// put("propertiesName", "ro.product.board")
// put("propertiesValue", bo)
// })
// put(JSONObject().apply {
// put("propertiesName", "ro.build.tags")
// put("propertiesValue", )
// })
put(JSONObject().apply {
put("propertiesName", "ro.build.fingerprint")
put("propertiesValue", ro_build_fingerprint)
})
// put(JSONObject().apply {
// put("propertiesName", "ro.build.date.utc")
// put("propertiesValue", da)
// })
// put(JSONObject().apply {
// put("propertiesName", "ro.build.user")
// put("propertiesValue", user)
// })
// put(JSONObject().apply {
// put("propertiesName", "ro.build.host")
// put("propertiesValue", device.expand.)
// })
// put(JSONObject().apply {
// put("propertiesName", "ro.build.description")
// put("propertiesValue", des)
// })
put(JSONObject().apply {
put("propertiesName", "ro.build.version.incremental")
put("propertiesValue", ro_build_version_incremental)
})
// put(JSONObject().apply {
// put("propertiesName", "ro.build.version.codename")
// put("propertiesValue", device.cod)
// })
})
put("settingPropertiesList", JSONArray().apply {
put(JSONObject().apply {
put("propertiesName", "ssaid/${recordPackageName}")
put("propertiesValue", androidId)
})
// put(JSONObject().apply {
// put("propertiesName", "bt/mac")
// put("propertiesValue", mac)
// })
put(JSONObject().apply {
put("propertiesName", "language")
put("propertiesValue", langCode)
})
put(JSONObject().apply {
put("propertiesName", "timezone")
put("propertiesValue", timeZone)
})
// put(JSONObject().apply {
// put("propertiesName", "systemvolume")
// put("propertiesValue", device.expand.)
// })
})
put("oaidPropertiesList", JSONArray().apply {
put(JSONObject().apply {
put("propertiesName", "AAID")
put("propertiesValue", advertiserId)
})
})
}
}
val jsonString = jsonObject.toString()
val response = Api.updatePad(jsonString)
val dataList = response.data
if (response.isSuccess() && dataList != null && dataList.isNotEmpty()) {
val padTask = dataList[0]
scope.launch {
Log.d("TAG", "changeDevice: $padTask")
var loop = true
while (loop) {
delay(5000)
val result = Api.padTaskDetail(padTask.taskId)
if (result == 3) {
Log.d(
"ChangeDeviceInfoUtil",
"changeDeviceInfo changeDeviceInfo success"
)
loop = false
callback.changeSuccess()
} else if (result == -1) {
Log.d("ChangeDeviceInfoUtil", "changeDeviceInfo changeDeviceInfo fail")
loop = false
callback.changeFailed()
}
}
}
}
} catch (e: JSONException) {
e.printStackTrace()
callback.changeFailed()
}
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.example.retention.utils; package com.example.studyapp.utils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;

View File

@ -1,15 +1,9 @@
package com.example.retention.config; package com.example.studyapp.utils;
import android.util.Log; import android.util.Log;
import com.example.retention.utils.LogFileUtil;
/** import com.example.studyapp.MainActivity;
* @Time: 2025-08-09 21:08
* @Creator: 初屿贤
* @File: ewfw
* @Project: study.App
* @Description:
*/
public class CountryCode { public class CountryCode {
public static final int DEVICE_TYPE = 2; public static final int DEVICE_TYPE = 2;
static final String US = "US"; static final String US = "US";

View File

@ -0,0 +1,52 @@
package com.example.studyapp.utils;
import java.io.File;
public class FileUtils {
/**
* 获取文件的父目录路径
* @param filePath 文件完整路径
* @return 父目录路径如果失败返回null
*/
public static String getParentDirectory(String filePath) {
if (filePath == null || filePath.isEmpty()) {
return null;
}
File file = new File(filePath);
File parent = file.getParentFile();
return parent != null ? parent.getAbsolutePath() : null;
}
/**
* 获取文件的父目录File对象
* @param file 文件对象
* @return 父目录File对象
*/
public static File getParentDirectory(File file) {
return file != null ? file.getParentFile() : null;
}
/**
* 获取多级上级目录
* @param file 文件对象
* @param levels 向上追溯的层级数
* @return 上级目录路径
*/
public static String getAncestorDirectory(File file, int levels) {
if (file == null || levels <= 0) {
return file != null ? file.getAbsolutePath() : null;
}
File parent = file;
for (int i = 0; i < levels; i++) {
parent = parent.getParentFile();
if (parent == null) {
break;
}
}
return parent != null ? parent.getAbsolutePath() : null;
}
}

View File

@ -1,4 +1,4 @@
package com.example.retention.utils; package com.example.studyapp.utils;
import android.util.Log; import android.util.Log;

View File

@ -1,12 +1,19 @@
package com.example.retention.utils; package com.example.studyapp.utils;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
public class IpUtil { public class IpUtil {
public static boolean isValidIPAddress(String ipAddress) { public static boolean isValidIPAddress(String ipAddress) {
@ -45,6 +52,49 @@ public class IpUtil {
} }
} }
public static String fetchGeoInfo() {
Request request = new Request.Builder()
.url("https://ipv4.geojs.io/v1/ip/geo.json")
.build();
OkHttpClient client = new OkHttpClient();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
Log.e("ClashUtil", "OkHttp request unsuccessful: " + response.code());
return null;
}
ResponseBody responseBody = response.body();
if (responseBody == null) {
Log.e("ClashUtil", "Response body is null");
return null;
}
String jsonData = responseBody.string();
Log.i("ClashUtil", "Geo info: " + jsonData);
return jsonData;
} catch (IOException e) {
Log.e("ClashUtil", "OkHttp request failed: ", e);
return null;
}
}
public static String getTimeZone(String geoInfo)
{
try {
if (!TextUtils.isEmpty(geoInfo))
{
JSONObject json = new JSONObject(geoInfo);
return json.optString("timezone", "");
}
} catch (Exception e) {
LogFileUtil.logAndWrite(Log.ERROR, "IpUtil", "getTimeZone: JSON error", e);
}
return "";
}
public static String checkClientIp(String excludeCountry) public static String checkClientIp(String excludeCountry)
{ {
try { try {

View File

@ -1,4 +1,4 @@
package com.example.retention.utils; package com.example.studyapp.utils;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.example.retention.utils; package com.example.studyapp.utils;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member; import java.lang.reflect.Member;

View File

@ -1,6 +1,8 @@
package com.example.retention.utils; package com.example.studyapp.utils;
import android.util.Log;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;

View File

@ -1,4 +1,6 @@
package com.example.retention.utils; package com.example.studyapp.utils;
import static java.security.AccessController.getContext;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.BufferedReader; import java.io.BufferedReader;

View File

@ -0,0 +1,64 @@
package com.example.studyapp.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipUtils {
/**
* 压缩目录为ZIP文件
* @param sourceDirPath 要压缩的目录路径/sdcard/MyFolder
* @param zipFilePath 生成的ZIP文件路径/sdcard/Archive.zip
* @throws IOException 如果操作失败
*/
public static void zipDirectory(String sourceDirPath, String zipFilePath) throws IOException {
File sourceDir = new File(sourceDirPath);
if (!sourceDir.exists() || !sourceDir.isDirectory()) {
throw new IOException("源目录不存在或不是目录");
}
try (FileOutputStream fos = new FileOutputStream(zipFilePath);
ZipOutputStream zos = new ZipOutputStream(fos)) {
addDirectoryToZip(sourceDir, sourceDir, zos);
}
}
/**
* 递归添加目录内容到ZIP
*/
private static void addDirectoryToZip(File rootDir, File currentDir, ZipOutputStream zos) throws IOException {
File[] files = currentDir.listFiles();
if (files == null) return;
for (File file : files) {
if (file.isDirectory()) {
addDirectoryToZip(rootDir, file, zos); // 递归处理子目录
} else {
addFileToZip(rootDir, file, zos);
}
}
}
/**
* 添加单个文件到ZIP
*/
private static void addFileToZip(File rootDir, File file, ZipOutputStream zos) throws IOException {
try (FileInputStream fis = new FileInputStream(file)) {
// 计算相对路径确保ZIP内目录结构正确
String relativePath = rootDir.toURI().relativize(file.toURI()).getPath();
ZipEntry zipEntry = new ZipEntry(relativePath);
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
zos.closeEntry();
}
}
}

View File

@ -1,4 +1,4 @@
package com.example.retention.worker; package com.example.studyapp.worker;
import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityService;
import android.content.ComponentName; import android.content.ComponentName;
@ -10,7 +10,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.work.CoroutineWorker; import androidx.work.CoroutineWorker;
import androidx.work.WorkerParameters; import androidx.work.WorkerParameters;
import com.example.retention.service.MyAccessibilityService; import com.example.studyapp.service.MyAccessibilityService;
import kotlin.coroutines.Continuation; import kotlin.coroutines.Continuation;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@ -1,102 +1,87 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="16dp" android:padding="16dp"
tools:context="com.example.retention.MainActivity"> tools:context=".MainActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:divider="@android:color/darker_gray" android:orientation="vertical"
android:dividerPadding="8dp" android:divider="@android:color/darker_gray"
android:orientation="vertical" android:showDividers="middle"
android:showDividers="middle"> android:dividerPadding="8dp">
<!-- VPN 分组标题 --> <!-- VPN 分组标题 -->
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:text="VPN 操作"
android:padding="8dp" android:padding="8dp"
android:text="VPN 操作" android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
android:visibility="gone" />
<!-- VPN 按钮 --> <!-- VPN 按钮 -->
<Button <Button
android:id="@+id/run_script_button" android:id="@+id/run_script_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="运行 脚本" android:text="运行 脚本" />
android:visibility="gone" />
<Button <Button
android:id="@+id/connectVpnButton" android:id="@+id/connectVpnButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="开启 VPN" android:text="开启 VPN" />
android:visibility="gone" />
<Button <Button
android:id="@+id/disconnectVpnButton" android:id="@+id/disconnectVpnButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="断开 VPN" android:text="断开 VPN" />
android:visibility="gone" />
<Button <Button
android:id="@+id/switchVpnButton" android:id="@+id/switchVpnButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="切换 VPN" android:text="切换 VPN" />
android:visibility="gone" />
<!-- 设备信息分组标题 --> <!-- 设备信息分组标题 -->
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:text="设备信息操作"
android:padding="8dp" android:padding="8dp"
android:text="设备信息操作" android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
android:visibility="gone" />
<!-- 设备信息按钮 --> <!-- 设备信息按钮 -->
<Button <Button
android:id="@+id/modifyDeviceInfoButton" android:id="@+id/modifyDeviceInfoButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="修改设备信息" android:text="修改设备信息" />
android:visibility="gone" />
<Button <Button
android:id="@+id/resetDeviceInfoButton" android:id="@+id/resetDeviceInfoButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="重置设备信息" android:text="重置设备信息" />
android:visibility="gone" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:text="Bigo操作"
android:padding="8dp" android:padding="8dp"
android:text="Bigo操作" android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<Button <Button
android:id="@+id/execute_button" android:id="@+id/execute_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="一键执行" /> android:text="一键执行" />
<Button <Button
android:id="@+id/stop_execute_button" android:id="@+id/stop_execute_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="停止执行" /> android:text="停止执行" />
</LinearLayout> </LinearLayout>

View File

@ -1,4 +1,8 @@
<resources> <resources>
<string name="app_name">Retention</string> <string name="app_name">study_app</string>
<string name="accessibility_service_description">This is an accessibility service.</string> <string name="accessibility_service_description">This is an accessibility service.</string>
<string name="task_executing">任务执行中</string>
<string name="button_not_found">按钮未找到</string>
<string name="network_unavailable">网络不可用</string>
<string name="stop_button_not_found">停止按钮未找到</string>
</resources> </resources>

View File

@ -1,18 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<!-- res/xml/network_security_config.xml -->
<network-security-config> <network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true"> <domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">47.236.153.142</domain>
<domain includeSubdomains="true">8.211.204.20</domain>
<domain includeSubdomains="true">127.0.0.1</domain> <domain includeSubdomains="true">127.0.0.1</domain>
<domain includeSubdomains="true">8.217.137.25</domain>
<domain includeSubdomains="true">47.238.96.231</domain>
<domain includeSubdomains="true">192.168.30.80</domain>
<domain includeSubdomains="true">39.103.73.250</domain> <!-- 你的特定 IP 地址 -->
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -1,4 +1,4 @@
package com.example.retention; package com.example.studyapp;
import org.junit.Test; import org.junit.Test;

View File

@ -1,4 +1,4 @@
package com.example.retention.task; package com.example.studyapp.task;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -131,14 +131,14 @@ public class TaskUtilTest {
// 运行上传方法 // 运行上传方法
String taskId = "asddasdasd"; String taskId = "asddasdasd";
TaskUtil.postDeviceInfo("b3d893cf9de3a85a", taskId, "com.example.retention"); //TaskUtil.postDeviceInfo("b3d893cf9de3a85a", taskId, "com.example.studyapp", ipInfo);
} }
@Test @Test
public void testGetDeviceInfoSync_Success() throws Exception { public void testGetDeviceInfoSync_Success() throws Exception {
// 运行上传方法 // 运行上传方法
// TaskUtil.getDeviceInfoSync("b3d893cf9de3a85a"); //TaskUtil.getDeviceInfoSync("b3d893cf9de3a85a");
} }
// @Test // @Test

View File

@ -7,4 +7,5 @@ buildscript {
} }
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
} }

2
cmd
View File

@ -1,4 +1,4 @@
V2243A:/ # ls -l /data/user/0/com.example.retention/files/ V2243A:/ # ls -l /data/user/0/com.example.studyapp/files/
total 37516 total 37516
-rw-rw-rw- 1 u0_a135 u0_a135 2398 2025-05-27 10:43 config.json -rw-rw-rw- 1 u0_a135 u0_a135 2398 2025-05-27 10:43 config.json
-rw------- 1 u0_a135 u0_a135 24 2025-05-27 10:42 profileInstalled -rw------- 1 u0_a135 u0_a135 24 2025-05-27 10:42 profileInstalled

288
err.log
View File

@ -1,205 +1,83 @@
2025-06-23 17:01:42.314 297-3144 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0 ~\AndroidStudioProjects\Agent-bigo git:[Added]
2025-06-23 17:01:42.320 297-462 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0 gradle assembleRelease
2025-06-23 17:01:42.569 297-3144 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0
2025-06-23 17:01:43.415 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.SubSettings com.android.settings/com.android.settings.SubSettings Starting a Gradle Daemon (subsequent builds will be faster)
2025-06-23 17:01:44.188 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=6066, ts=1750669303948, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=6066, ts=1750669303948, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=6066, ts=1750669303948, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=209, time=9, total=6066, ts=1750669303948, pr=20, ni=0, virt=14336, shr=111, s=S, cpu=36.0, mem=6.3, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=307, time=374, total=6066, ts=1750669303948, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.3, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=6066, ts=1750669303948, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)] Build was configured to prefer settings repositories over project repositories but repository 'MavenLocal' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:45.397 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/androidx.appcompat.app.AlertDialog com.android.settings/androidx.appcompat.app.AlertDialog Build was configured to prefer settings repositories over project repositories but repository 'MavenRepo' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:47.565 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/androidx.appcompat.app.AlertDialog com.android.settings/androidx.appcompat.app.AlertDialog Build was configured to prefer settings repositories over project repositories but repository 'Google' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:51.409 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.SubSettings com.android.settings/com.android.settings.SubSettings Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:54.631 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.Settings$AccessibilitySettingsActivity com.android.settings/com.android.settings.Settings$AccessibilitySettingsActivity Build was configured to prefer settings repositories over project repositories but repository 'Alibaba' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:54.661 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.Settings$AccessibilitySettingsActivity com.android.settings/com.android.settings.Settings$AccessibilitySettingsActivity Build was configured to prefer settings repositories over project repositories but repository 'Bstek' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:54.733 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=6066, ts=1750669314510, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=6066, ts=1750669314510, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=6066, ts=1750669314510, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=203, time=13, total=6066, ts=1750669314510, pr=20, ni=0, virt=14336, shr=111, s=S, cpu=3.8, mem=6.1, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=307, time=374, total=6066, ts=1750669314510, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.3, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=6066, ts=1750669314510, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)] Build was configured to prefer settings repositories over project repositories but repository 'MavenLocal' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:56.366 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.Settings$AppManageExternalStorageActivity com.android.settings/com.android.settings.Settings$AppManageExternalStorageActivity Build was configured to prefer settings repositories over project repositories but repository 'MavenRepo' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:56.413 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.settings/com.android.settings.Settings$AppManageExternalStorageActivity com.android.settings/com.android.settings.Settings$AppManageExternalStorageActivity Build was configured to prefer settings repositories over project repositories but repository 'Google' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:59.870 1532-1532 NotificationObserver org.autojs.autojs6 D onNotification: [Storage Permissions granted]; EventType: TYPE_NOTIFICATION_STATE_CHANGED; EventTime: 870589469; PackageName: com.example.retention; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.Toast; Text: [Storage Permissions granted]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: false; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: 0; ScrollY: 0; MaxScrollX: 0; MaxScrollY: 0; ScrollDeltaX: -1; ScrollDeltaY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0 Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:01:59.924 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.example.retention/com.example.retention.MainActivity com.example.retention/com.example.retention.MainActivity Build was configured to prefer settings repositories over project repositories but repository 'Alibaba' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:05.235 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=6058, ts=1750669325040, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=6058, ts=1750669325040, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=6058, ts=1750669325040, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=206, time=19, total=6058, ts=1750669325040, pr=10, ni=-10, virt=14336, shr=114, s=S, cpu=104.0, mem=6.2, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=308, time=374, total=6058, ts=1750669325040, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.3, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=6058, ts=1750669325040, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)] Build was configured to prefer settings repositories over project repositories but repository 'Bstek' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:08.114 297-16062 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0
2025-06-23 17:02:08.119 297-462 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0 > Task :app:processReleaseMainManifest
2025-06-23 17:02:08.365 297-11816 ClipboardService system_server E Denying clipboard access to org.autojs.autojs6, application is not in focus nor is it a system service for user 0 C:\Users\yjj38\AndroidStudioProjects\Agent-bigo\app\src\main\AndroidManifest.xml:15:1-107 Warning:
2025-06-23 17:02:09.928 1532-1532 NotificationObserver org.autojs.autojs6 D onNotification: [任务正在执行]; EventType: TYPE_NOTIFICATION_STATE_CHANGED; EventTime: 870599525; PackageName: com.example.retention; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.Toast; Text: [任务正在执行]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: false; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: 0; ScrollY: 0; MaxScrollX: 0; MaxScrollY: 0; ScrollDeltaX: -1; ScrollDeltaY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0 Element uses-permission#android.permission.MANAGE_EXTERNAL_STORAGE at AndroidManifest.xml:15:1-107 duplicated with element declared at AndroidManifest.xml:9:1-78
2025-06-23 17:02:12.071 51143-51376 isAppInstalled com.example.retention D Checking if app is installed: org.autojs.autojs6
2025-06-23 17:02:12.075 51143-51376 ShellUtils com.example.retention D execRootCmdAndGetResult - Started execution for command: pm list packages | grep org.autojs.autojs6 > Task :app:validateSigningRelease FAILED
2025-06-23 17:02:12.120 51143-51376 ShellUtils com.example.retention D Shell Output: package:org.autojs.autojs6
2025-06-23 17:02:12.223 51143-51376 isAppInstalled com.example.retention D App is installed: org.autojs.autojs6 [Incubating] Problems report is available at: file:///C:/Users/yjj38/AndroidStudioProjects/Agent-bigo/build/reports/problems/problems-report.html
2025-06-23 17:02:12.225 297-3145 ActivityTaskManager system_server I START u0 {flg=0x10000000 cmp=org.autojs.autojs6/org.autojs.autojs.external.open.RunIntentActivity (has extras)} from uid 10234
2025-06-23 17:02:12.240 1532-1532 App org.autojs.autojs6 D setCurrentActivity: org.autojs.autojs.external.open.RunIntentActivity@aad3ba2 FAILURE: Build failed with an exception.
2025-06-23 17:02:12.243 1532-1532 ScriptEngineService org.autojs.autojs6 D JavaScriptSource: true
2025-06-23 17:02:12.246 611-2827 TelephonyPermissions com.android.phone W reportAccessDeniedToReadIdentifiers:org.autojs.autojs6:getImeiForSlot:1 * What went wrong:
2025-06-23 17:02:12.249 297-6814 TelephonyPermissions system_server W reportAccessDeniedToReadIdentifiers:org.autojs.autojs6:getSerial:-1 Execution failed for task ':app:validateSigningRelease'.
2025-06-23 17:02:12.250 1532-51468 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 1 > Keystore file 'C:\Users\yjj38\AndroidStudioProjects\Agent-bigo\app\agent_add.jks' not found for signing config 'release'.
2025-06-23 17:02:12.250 1532-51468 ContextFactory org.autojs.autojs6 D onContextCreated: count = 1
2025-06-23 17:02:12.294 1532-51468 ScriptEngineService org.autojs.autojs6 D onStart * Try:
2025-06-23 17:02:12.299 1532-51468 GlobalConsole org.autojs.autojs6 D 09:02:12.299/V: Running [$sdcard/script/main.js]. > Run with --stacktrace option to get the stack trace.
2025-06-23 17:02:12.307 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.example.retention/com.example.retention.MainActivity com.example.retention/com.example.retention.MainActivity > Run with --info or --debug option to get more log output.
2025-06-23 17:02:12.307 1532-51469 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 2 > Run with --scan to get full insights.
2025-06-23 17:02:12.308 1532-51470 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 3 > Get more help at https://help.gradle.org.
2025-06-23 17:02:12.308 1532-51470 ContextFactory org.autojs.autojs6 D onContextCreated: count = 3
2025-06-23 17:02:12.308 1532-51469 ContextFactory org.autojs.autojs6 D onContextCreated: count = 2 Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
2025-06-23 17:02:12.309 1532-51471 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 4
2025-06-23 17:02:12.309 1532-51471 ContextFactory org.autojs.autojs6 D onContextCreated: count = 4 You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
2025-06-23 17:02:12.313 1532-51469 App org.autojs.autojs6 D getCurrentActivity: org.autojs.autojs.external.open.RunIntentActivity@aad3ba2
2025-06-23 17:02:12.313 297-6814 ActivityTaskManager system_server I START u0 {flg=0x10000000 cmp=org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity (has extras)} from uid 10100 For more on this, please refer to https://docs.gradle.org/8.14/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
2025-06-23 17:02:12.314 297-6814 ActivityTaskManager system_server I Launching r: ActivityRecord{a98f3cd u0 org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity} from background: ActivityRecord{91ff715 u0 org.autojs.autojs6/org.autojs.autojs.external.open.RunIntentActivity} t9913 f}}. New task: true
2025-06-23 17:02:12.326 1532-1532 App org.autojs.autojs6 D setCurrentActivity: org.autojs.autojs.core.activity.StartForResultActivity@2e7ffe4 BUILD FAILED in 37s
2025-06-23 17:02:12.336 1532-1532 App org.autojs.autojs6 D setCurrentActivity: org.autojs.autojs.core.activity.StartForResultActivity@2e7ffe4 38 actionable tasks: 37 executed, 1 up-to-date
2025-06-23 17:02:12.337 1532-55975 Parcel org.autojs.autojs6 W Expecting binder but got null! ~\AndroidStudioProjects\Agent-bigo git:[Added]
2025-06-23 17:02:12.339 297-11816 CoreBackPreview system_server D Window{e9771a6 u0 org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity}: Setting back callback OnBackInvokedCallbackInfo{mCallback=android.window.IOnBackInvokedCallback$Stub$Proxy@1638f94, mPriority=0} gradle assembleRelease
2025-06-23 17:02:12.340 1532-1532 App org.autojs.autojs6 D setCurrentActivity: null Build was configured to prefer settings repositories over project repositories but repository 'MavenLocal' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.343 1532-51478 TrafficStats org.autojs.autojs6 D tagSocket(245) with statsTag=0x4d2, statsUid=-1 Build was configured to prefer settings repositories over project repositories but repository 'MavenRepo' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.343 1532-51478 TrafficStats org.autojs.autojs6 I tagSocketFd(245, 1234, -1) failed with errno-9 Build was configured to prefer settings repositories over project repositories but repository 'Google' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.348 1532-55975 OpenGLRenderer org.autojs.autojs6 E Unable to match the desired swap behavior. Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.421 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.systemui/android.app.AlertDialog com.android.systemui/android.app.AlertDialog Build was configured to prefer settings repositories over project repositories but repository 'Alibaba' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.851 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity Build was configured to prefer settings repositories over project repositories but repository 'Bstek' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:12.871 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity Build was configured to prefer settings repositories over project repositories but repository 'MavenLocal' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:13.310 1532-51470 GlobalConsole org.autojs.autojs6 D 09:02:13.310/D: 计时线程: 0/600秒 Build was configured to prefer settings repositories over project repositories but repository 'MavenRepo' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:13.321 1532-1532 NotificationObserver org.autojs.autojs6 D onNotification: [计时线程: 0/600秒, AutoJs6]; EventType: TYPE_NOTIFICATION_STATE_CHANGED; EventTime: 870602921; PackageName: org.autojs.autojs6; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.Toast; Text: [计时线程: 0/600秒, AutoJs6]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: false; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: 0; ScrollY: 0; MaxScrollX: 0; MaxScrollY: 0; ScrollDeltaX: -1; ScrollDeltaY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0 Build was configured to prefer settings repositories over project repositories but repository 'Google' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:13.827 1532-51471 GlobalConsole org.autojs.autojs6 D 09:02:13.827/D: 点击START NOW Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:13.828 1532-51471 GlobalActionAutomator org.autojs.autojs6 D Path moved to (889.0, 1173.0) Build was configured to prefer settings repositories over project repositories but repository 'Alibaba' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:13.829 1532-51471 GlobalActionAutomator org.autojs.autojs6 D dispatchGesture: true Build was configured to prefer settings repositories over project repositories but repository 'Bstek' was added by initialization script 'C:\Program Files\gradle-8.14\init.d\init.gradle'
2025-06-23 17:02:14.058 297-3144 DisplayDeviceRepository system_server I Display device added: DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0} > Task :app:validateSigningRelease FAILED
2025-06-23 17:02:14.062 297-3144 LogicalDisplayMapper system_server I Adding new display: 14: DisplayInfo{"ScreenCapturer", displayId 14", displayGroupId -1, FLAG_PRIVATE, FLAG_PRESENTATION, real 1080 x 1920, largest app 1080 x 1920, smallest app 1080 x 1920, appVsyncOff 0, presDeadline 16666666, mode 14, defaultMode 14, modes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], hdrCapabilities null, userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state ON, committedState UNKNOWN, type VIRTUAL, uniqueId "virtual:org.autojs.autojs6,10100,ScreenCapturer,0", app 1080 x 1920, density 320 (320.0 x 320.0) dpi, layerStack 14, colorMode 0, supportedColorModes [0], deviceProductInfo null, owner org.autojs.autojs6 (uid 10100), removeMode 1, refreshRateOverride 0.0, brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, installOrientation ROTATION_0}
2025-06-23 17:02:14.070 297-313 DisplayManagerService system_server W Display DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0} does not support input device matching. [Incubating] Problems report is available at: file:///C:/Users/yjj38/AndroidStudioProjects/Agent-bigo/build/reports/problems/problems-report.html
2025-06-23 17:02:14.071 297-313 DisplayManagerService system_server W Display DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0} does not support input device matching.
2025-06-23 17:02:14.072 297-313 DisplayDevice system_server I [14] Layerstack set to 14 for virtual:org.autojs.autojs6,10100,ScreenCapturer,0 FAILURE: Build failed with an exception.
2025-06-23 17:02:14.073 297-313 DisplayManagerService system_server W Display DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0} does not support input device matching.
2025-06-23 17:02:14.075 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 5 * What went wrong:
2025-06-23 17:02:14.075 1532-1532 ContextFactory org.autojs.autojs6 D onContextCreated: count = 5 Execution failed for task ':app:validateSigningRelease'.
2025-06-23 17:02:14.076 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 4 > Keystore file 'C:\Users\yjj38\AndroidStudioProjects\Agent-bigo\app\agent_add.jks' not found for signing config 'release'.
2025-06-23 17:02:14.076 1532-1532 ContextFactory org.autojs.autojs6 D onContextReleased: count = 4
2025-06-23 17:02:14.078 297-316 DisplayManagerService system_server W Display DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0} does not support input device matching. * Try:
2025-06-23 17:02:14.079 297-334 DisplayPow...roller[14] system_server I BrightnessEvent: disp=14, physDisp=virtual:org.autojs.autojs6,10100,ScreenCapturer,0, brt=0.035433073, initBrt=NaN, rcmdBrt=NaN, preBrt=NaN, lux=0.0, preLux=0.0, hbmMax=1.0, hbmMode=off, rbcStrength=-1, powerFactor=1.0, thrmMax=1.0, wasShortTermModelActive=false, flags=, reason=manual, autoBrightness=false > Run with --stacktrace option to get the stack trace.
2025-06-23 17:02:14.079 297-334 DisplayPow...roller[14] system_server I BrightnessEvent: disp=14, physDisp=virtual:org.autojs.autojs6,10100,ScreenCapturer,0, brt=0.035433073, initBrt=0.035433073, rcmdBrt=NaN, preBrt=NaN, lux=0.0, preLux=0.0, hbmMax=1.0, hbmMode=off, rbcStrength=-1, powerFactor=1.0, thrmMax=1.0, wasShortTermModelActive=false, flags=, reason=manual, autoBrightness=false > Run with --info or --debug option to get more log output.
2025-06-23 17:02:14.079 297-334 DisplayPow...roller[14] system_server I BrightnessEvent: disp=14, physDisp=virtual:org.autojs.autojs6,10100,ScreenCapturer,0, brt=0.035433073, initBrt=0.035433073, rcmdBrt=NaN, preBrt=NaN, lux=0.0, preLux=0.0, hbmMax=1.0, hbmMode=off, rbcStrength=-1, powerFactor=1.0, thrmMax=1.0, wasShortTermModelActive=false, flags=, reason=manual, autoBrightness=false > Run with --scan to get full insights.
2025-06-23 17:02:14.079 297-334 DisplayPow...roller[14] system_server I BrightnessEvent: disp=14, physDisp=virtual:org.autojs.autojs6,10100,ScreenCapturer,0, brt=0.035433073, initBrt=0.035433073, rcmdBrt=NaN, preBrt=NaN, lux=0.0, preLux=0.0, hbmMax=1.0, hbmMode=off, rbcStrength=-1, powerFactor=1.0, thrmMax=1.0, wasShortTermModelActive=false, flags=, reason=manual, autoBrightness=false > Get more help at https://help.gradle.org.
2025-06-23 17:02:14.079 297-334 DisplayPow...roller[14] system_server I BrightnessEvent: disp=14, physDisp=virtual:org.autojs.autojs6,10100,ScreenCapturer,0, brt=0.035433073, initBrt=0.035433073, rcmdBrt=NaN, preBrt=NaN, lux=0.0, preLux=0.0, hbmMax=1.0, hbmMode=off, rbcStrength=-1, powerFactor=1.0, thrmMax=1.0, wasShortTermModelActive=false, flags=, reason=manual, autoBrightness=false
2025-06-23 17:02:14.080 297-3144 ActivityTaskManager system_server W Duplicate finish request for r=ActivityRecord{a98f3cd u0 org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity} t9914 f}} Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
2025-06-23 17:02:14.081 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 5
2025-06-23 17:02:14.081 1532-1532 ContextFactory org.autojs.autojs6 D onContextCreated: count = 5 You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
2025-06-23 17:02:14.081 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 4
2025-06-23 17:02:14.081 1532-1532 ContextFactory org.autojs.autojs6 D onContextReleased: count = 4 For more on this, please refer to https://docs.gradle.org/8.14/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
2025-06-23 17:02:14.082 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextCreated: count = 5
2025-06-23 17:02:14.082 1532-1532 ContextFactory org.autojs.autojs6 D onContextCreated: count = 5 BUILD FAILED in 6s
2025-06-23 17:02:14.082 1532-1532 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 4 39 actionable tasks: 10 executed, 29 up-to-date
2025-06-23 17:02:14.082 1532-1532 ContextFactory org.autojs.autojs6 D onContextReleased: count = 4
2025-06-23 17:02:14.083 1532-51469 GlobalConsole org.autojs.autojs6 D 09:02:14.082/D: 首次 Threds errorFailed to invoke method app.viewFile. Cannot view /sdcard/script/launcher-release.apk as it doesn't exist (/storage/emulated/0/script/main.js#647)
2025-06-23 17:02:14.083 1532-51469 GlobalConsole org.autojs.autojs6 D 09:02:14.083/D: 首次 脚本线程 脚本catch结束
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W org.mozilla.javascript.WrappedException: Wrapped org.autojs.autojs.runtime.exception.ScriptInterruptedException (/storage/emulated/0/script/main.js#105)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.augmentFunctionsBy$lambda$24$lambda$23(Augmentable.kt:432)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.$r8$lambda$Fn1T-F1qaCuaZ27e1ntAmyEtA5M(Unknown Source:0)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable$$ExternalSyntheticLambda3.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$InternalBaseFunction.callFunc$app_appRelease(RhinoUtils.kt:877)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$newBaseFunction$5.call(RhinoUtils.kt:237)
2025-06-23 17:02:14.084 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.doCallByteCode(Interpreter.java:3019)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1973)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at script(/storage/emulated/0/script/main.js:105)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:1161)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:87)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:347)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:4450)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction$lambda$7(RhinoUtils.kt:169)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.$r8$lambda$f8ShGIQnCN3BfY5ui2ZDzafysTo(Unknown Source:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$$ExternalSyntheticLambda3.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.withRhinoContext(RhinoUtils.kt:754)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:164)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:153)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:141)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.coerceRunnable$lambda$26(RhinoUtils.kt:435)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.$r8$lambda$spHwZG5wVYypfhMmC-EzLoJxvzU(Unknown Source:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$$ExternalSyntheticLambda1.run(D8$$SyntheticClass:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at android.os.Handler.handleCallback(Handler.java:942)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at android.os.Handler.dispatchMessage(Handler.java:99)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at android.os.Looper.loopOnce(Looper.java:201)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at android.os.Looper.loop(Looper.java:288)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.core.looper.TimerThread.run(TimerThread.kt:56)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W Caused by: org.autojs.autojs.runtime.exception.ScriptInterruptedException
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.ScriptRuntime.sleep(ScriptRuntime.kt:507)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.sleep$lambda$20(Global.kt:244)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.$r8$lambda$OWAsxQgJG0C1vfHeAuhT_906-VY(Unknown Source:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion$$ExternalSyntheticLambda19.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.extension.FlexibleArray$Companion.unwrapAndInvokeAll(FlexibleArray.kt:76)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.extension.FlexibleArray$Companion.ensureArgumentsLengthInRange(FlexibleArray.kt:56)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.sleep(Global.kt:224)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global.sleep(Unknown Source:2)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at java.lang.reflect.Method.invoke(Native Method)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.augmentFunctionsBy$lambda$24$lambda$23(Augmentable.kt:415)
2025-06-23 17:02:14.085 1532-51470 System.err org.autojs.autojs6 W ... 25 more
2025-06-23 17:02:14.085 1532-51470 Loopers org.autojs.autojs6 D notifyThreadExit: Thread[ScriptThread-8[/storage/emulated/0/script/main.js] (Spawn-1),5]
2025-06-23 17:02:14.085 1532-51470 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 3
2025-06-23 17:02:14.085 1532-51470 ContextFactory org.autojs.autojs6 D onContextReleased: count = 3
2025-06-23 17:02:14.085 1532-51469 Loopers org.autojs.autojs6 D notifyThreadExit: Thread[ScriptThread-8[/storage/emulated/0/script/main.js] (Spawn-0),5]
2025-06-23 17:02:14.085 1532-51469 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 2
2025-06-23 17:02:14.085 1532-51469 ContextFactory org.autojs.autojs6 D onContextReleased: count = 2
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W org.mozilla.javascript.WrappedException: Wrapped org.autojs.autojs.runtime.exception.ScriptInterruptedException (/storage/emulated/0/script/main.js#160)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.augmentFunctionsBy$lambda$24$lambda$23(Augmentable.kt:432)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.$r8$lambda$Fn1T-F1qaCuaZ27e1ntAmyEtA5M(Unknown Source:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable$$ExternalSyntheticLambda3.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$InternalBaseFunction.callFunc$app_appRelease(RhinoUtils.kt:877)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$newBaseFunction$5.call(RhinoUtils.kt:237)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.doCallByteCode(Interpreter.java:3019)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1973)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at script(/storage/emulated/0/script/main.js:160)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:1161)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:87)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:347)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:4450)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction$lambda$7(RhinoUtils.kt:169)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.$r8$lambda$f8ShGIQnCN3BfY5ui2ZDzafysTo(Unknown Source:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$$ExternalSyntheticLambda3.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.withRhinoContext(RhinoUtils.kt:754)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:164)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:153)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.callFunction(RhinoUtils.kt:141)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.coerceRunnable$lambda$26(RhinoUtils.kt:435)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils.$r8$lambda$spHwZG5wVYypfhMmC-EzLoJxvzU(Unknown Source:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.util.RhinoUtils$$ExternalSyntheticLambda1.run(D8$$SyntheticClass:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at android.os.Handler.handleCallback(Handler.java:942)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at android.os.Handler.dispatchMessage(Handler.java:99)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at android.os.Looper.loopOnce(Looper.java:201)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at android.os.Looper.loop(Looper.java:288)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.core.looper.TimerThread.run(TimerThread.kt:56)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W Caused by: org.autojs.autojs.runtime.exception.ScriptInterruptedException
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.ScriptRuntime.sleep(ScriptRuntime.kt:507)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.sleep$lambda$20(Global.kt:244)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.$r8$lambda$OWAsxQgJG0C1vfHeAuhT_906-VY(Unknown Source:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion$$ExternalSyntheticLambda19.invoke(D8$$SyntheticClass:0)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.extension.FlexibleArray$Companion.unwrapAndInvokeAll(FlexibleArray.kt:76)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.extension.FlexibleArray$Companion.ensureArgumentsLengthInRange(FlexibleArray.kt:56)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global$Companion.sleep(Global.kt:224)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.global.Global.sleep(Unknown Source:2)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at java.lang.reflect.Method.invoke(Native Method)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W at org.autojs.autojs.runtime.api.augment.Augmentable.augmentFunctionsBy$lambda$24$lambda$23(Augmentable.kt:415)
2025-06-23 17:02:14.086 1532-51471 System.err org.autojs.autojs6 W ... 25 more
2025-06-23 17:02:14.086 1532-51471 Loopers org.autojs.autojs6 D notifyThreadExit: Thread[ScriptThread-8[/storage/emulated/0/script/main.js] (Spawn-2),5]
2025-06-23 17:02:14.086 1532-51471 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 1
2025-06-23 17:02:14.086 1532-51471 ContextFactory org.autojs.autojs6 D onContextReleased: count = 1
2025-06-23 17:02:14.087 1532-51468 GlobalConsole org.autojs.autojs6 D 09:02:14.087/V: [$sdcard/script/main.js] finished in 1.787 seconds.
2025-06-23 17:02:14.087 1532-51468 RunnableJSExecution org.autojs.autojs6 D Engine destroy
2025-06-23 17:02:14.087 1532-51468 ScriptRuntime org.autojs.autojs6 D on exit
2025-06-23 17:02:14.087 1532-51468 Loopers org.autojs.autojs6 D recycle
2025-06-23 17:02:14.089 297-3143 DisplayDeviceRepository system_server I Display device removed: DisplayDeviceInfo{"ScreenCapturer": uniqueId="virtual:org.autojs.autojs6,10100,ScreenCapturer,0", 1080 x 1920, modeId 14, defaultModeId 14, supportedModes [{id=14, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[]}], colorMode 0, supportedColorModes [0], hdrCapabilities null, allmSupported false, gameContentTypeSupported false, density 320, 320.0 x 320.0 dpi, appVsyncOff 0, presDeadline 16666666, touch NONE, rotation 0, type VIRTUAL, deviceProductInfo null, state ON, committedState UNKNOWN, owner org.autojs.autojs6 (uid 10100), frameRateOverride , brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, FLAG_PRIVATE, FLAG_PRESENTATION, installOrientation 0}
2025-06-23 17:02:14.093 1532-51468 RhinoJavaScriptEngine org.autojs.autojs6 D on destroy
2025-06-23 17:02:14.093 1532-51468 AndroidContextFactory org.autojs.autojs6 D onContextReleased: count = 0
2025-06-23 17:02:14.093 1532-51468 ContextFactory org.autojs.autojs6 D onContextReleased: count = 0
2025-06-23 17:02:14.093 1532-5003 BufferQueueProducer org.autojs.autojs6 E [ImageReader-1080x1920f1m3-1532-8](id:5fc00000021,api:1,p:52,c:1532) dequeueBuffer: BufferQueue has been abandoned
2025-06-23 17:02:14.099 1532-1532 ActivityInfoProvider org.autojs.autojs6 D setLatestComponent: com.example.retention/com.example.retention.MainActivity com.example.retention/com.example.retention.MainActivity
2025-06-23 17:02:14.102 1532-5003 BpBinder org.autojs.autojs6 I onLastStrongRef automatically unlinking death recipients: <uncached descriptor>
2025-06-23 17:02:14.103 297-6815 CoreBackPreview system_server D Window{e9771a6 u0 org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity}: Setting back callback null
2025-06-23 17:02:14.104 297-6815 InputManager-JNI system_server W Input channel object 'e9771a6 org.autojs.autojs6/org.autojs.autojs.core.activity.StartForResultActivity (client)' was disposed without first being removed with the input manager!
2025-06-23 17:02:14.251 297-297 NotificationService system_server I Cannot find enqueued record for key: 0|org.autojs.autojs6|207|null|10100
2025-06-23 17:02:15.923 297-3145 NotificationService system_server W Toast already killed. pkg=org.autojs.autojs6 token=android.os.BinderProxy@fb6cbea
2025-06-23 17:02:15.934 1532-1532 NotificationObserver org.autojs.autojs6 D onNotification: [首次 Threds errorFailed to invoke method app.viewFile. Cannot view /sdcard/script/launcher-release.apk as it doesn't exist (/storage/emulated/0/script/main.js#647), AutoJs6]; EventType: TYPE_NOTIFICATION_STATE_CHANGED; EventTime: 870605533; PackageName: org.autojs.autojs6; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.Toast; Text: [首次 Threds errorFailed to invoke method app.viewFile. Cannot view /sdcard/script/launcher-release.apk as it doesn't exist (/storage/emulated/0/script/main.js#647), AutoJs6]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: false; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: 0; ScrollY: 0; MaxScrollX: 0; MaxScrollY: 0; ScrollDeltaX: -1; ScrollDeltaY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
2025-06-23 17:02:16.164 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=5956, ts=1750669335563, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=5956, ts=1750669335563, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=5956, ts=1750669335563, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=212, time=22, total=5956, ts=1750669335563, pr=10, ni=-10, virt=14336, shr=115, s=S, cpu=0.0, mem=6.4, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=314, time=374, total=5956, ts=1750669335563, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.5, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=5956, ts=1750669335563, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)]
2025-06-23 17:02:18.505 297-3143 NotificationService system_server W Toast already killed. pkg=org.autojs.autojs6 token=android.os.BinderProxy@c258390
2025-06-23 17:02:18.517 1532-1532 NotificationObserver org.autojs.autojs6 D onNotification: [首次 脚本线程 脚本catch结束, AutoJs6]; EventType: TYPE_NOTIFICATION_STATE_CHANGED; EventTime: 870608116; PackageName: org.autojs.autojs6; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.Toast; Text: [首次 脚本线程 脚本catch结束, AutoJs6]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: false; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: 0; ScrollY: 0; MaxScrollX: 0; MaxScrollY: 0; ScrollDeltaX: -1; ScrollDeltaY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
2025-06-23 17:02:21.098 297-3143 NotificationService system_server W Toast already killed. pkg=org.autojs.autojs6 token=android.os.BinderProxy@afcbf66
2025-06-23 17:02:23.095 1532-51478 TrafficStats org.autojs.autojs6 D tagSocket(92) with statsTag=0x4d2, statsUid=-1
2025-06-23 17:02:23.095 1532-51478 TrafficStats org.autojs.autojs6 I tagSocketFd(92, 1234, -1) failed with errno-9
2025-06-23 17:02:24.589 1532-51478 TrafficStats org.autojs.autojs6 D tagSocket(92) with statsTag=0x4d2, statsUid=-1
2025-06-23 17:02:24.589 1532-51478 TrafficStats org.autojs.autojs6 I tagSocketFd(92, 1234, -1) failed with errno-9
2025-06-23 17:02:26.771 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=6027, ts=1750669346510, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=6027, ts=1750669346510, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=6027, ts=1750669346510, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=212, time=22, total=6027, ts=1750669346510, pr=10, ni=-10, virt=14336, shr=115, s=S, cpu=0.0, mem=6.4, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=315, time=374, total=6027, ts=1750669346510, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.6, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=6027, ts=1750669346510, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)]
2025-06-23 17:02:35.613 1532-51478 TrafficStats org.autojs.autojs6 D tagSocket(92) with statsTag=0x4d2, statsUid=-1
2025-06-23 17:02:35.613 1532-51478 TrafficStats org.autojs.autojs6 I tagSocketFd(92, 1234, -1) failed with errno-9
2025-06-23 17:02:36.534 1532-51478 TrafficStats org.autojs.autojs6 D tagSocket(92) with statsTag=0x4d2, statsUid=-1
2025-06-23 17:02:36.535 1532-51478 TrafficStats org.autojs.autojs6 I tagSocketFd(92, 1234, -1) failed with errno-9
2025-06-23 17:02:37.413 2002-2092 MemInfoService com.android.expansiontools D memScan: [MemEntity(id=null, pid=35830, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=139, time=2, total=6034, ts=1750669357102, pr=20, ni=0, virt=1228, shr=84, s=S, cpu=0.0, mem=4.2, args=com.github.kr328.clash), MemEntity(id=null, pid=63204, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=123, time=92, total=6034, ts=1750669357102, pr=20, ni=0, virt=1126, shr=38, s=S, cpu=0.0, mem=3.7, args=com.github.kr328.clash:core), MemEntity(id=null, pid=63228, user=u0_a104, name=Clash for Android, pkg=com.github.kr328.clash, res=25, time=259, total=6034, ts=1750669357102, pr=20, ni=0, virt=704, shr=10, s=S, cpu=0.0, mem=0.7, args=libclash.so), MemEntity(id=null, pid=51143, user=u0_a234, name=Script helper, pkg=com.example.retention, res=212, time=22, total=6034, ts=1750669357102, pr=10, ni=-10, virt=14336, shr=115, s=S, cpu=0.0, mem=6.4, args=com.example.retention), MemEntity(id=null, pid=1532, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=315, time=374, total=6034, ts=1750669357102, pr=20, ni=0, virt=33792, shr=179, s=S, cpu=0.0, mem=9.6, args=org.autojs.autojs6), MemEntity(id=null, pid=56880, user=u0_a100, name=AutoJs6, pkg=org.autojs.autojs6, res=0, time=0, total=6034, ts=1750669357102, pr=20, ni=0, virt=10240, shr=0, s=S, cpu=0.0, mem=0.0, args=sh)]

View File

@ -8,6 +8,8 @@ material = "1.12.0"
activity = "1.10.1" activity = "1.10.1"
constraintlayout = "2.2.1" constraintlayout = "2.2.1"
playServicesAdsIdentifier = "18.2.0" playServicesAdsIdentifier = "18.2.0"
kotlin = "2.0.21"
coreKtx = "1.16.0"
[libraries] [libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
@ -18,6 +20,8 @@ material = { group = "com.google.android.material", name = "material", version.r
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
play-services-ads-identifier = { group = "com.google.android.gms", name = "play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" } play-services-ads-identifier = { group = "com.google.android.gms", name = "play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

248
main.js
View File

@ -1,155 +1,155 @@
// ========== 日志写入文件的封装 ========== // ========== 日志写入文件的封装 ==========
function log(...args) { function log(...args) {
const message = `[LOG] ${args.map(a => const message = `[LOG] ${args.map(a =>
typeof a === 'object' ? JSON.stringify(a) : a typeof a === 'object' ? JSON.stringify(a) : a
).join(' ')}`; ).join(' ')}`;
console.log(message); console.log(message);
files.append("/sdcard/output_log.txt", new Date().toISOString() + " " + message + "\n"); files.append("/sdcard/output_log.txt", new Date().toISOString() + " " + message + "\n");
} }
function error(...args) { function error(...args) {
const message = `[ERROR] ${args.map(a => const message = `[ERROR] ${args.map(a =>
typeof a === 'object' ? JSON.stringify(a) : a typeof a === 'object' ? JSON.stringify(a) : a
).join(' ')}`; ).join(' ')}`;
console.error(message); console.error(message);
files.append("/sdcard/output_log.txt", new Date().toISOString() + " " + message + "\n"); files.append("/sdcard/output_log.txt", new Date().toISOString() + " " + message + "\n");
} }
// ========== 网络请求封装 ========== // ========== 网络请求封装 ==========
function httpGetWithCallbacks(url, successCallback, errorCallback) { function httpGetWithCallbacks(url, successCallback, errorCallback) {
http.get(url, {}, (res, err) => { http.get(url, {}, (res, err) => {
if (err) { if (err) {
if (errorCallback) errorCallback(err); if (errorCallback) errorCallback(err);
} else { } else {
if (successCallback) successCallback(res); if (successCallback) successCallback(res);
} }
}); });
} }
function processIpApiWithCallbacks(url, processSuccessCallback, errorMsgPrefix, apiDoneCallback) { function processIpApiWithCallbacks(url, processSuccessCallback, errorMsgPrefix, apiDoneCallback) {
httpGetWithCallbacks(url, httpGetWithCallbacks(url,
(res) => { (res) => {
let operationSuccessful = false; let operationSuccessful = false;
if (!res) { if (!res) {
toast(errorMsgPrefix + "请求没有返回有效响应"); toast(errorMsgPrefix + "请求没有返回有效响应");
log(errorMsgPrefix + "请求没有返回有效响应"); log(errorMsgPrefix + "请求没有返回有效响应");
if (apiDoneCallback) apiDoneCallback(new Error("No valid response from request (res is null)")); if (apiDoneCallback) apiDoneCallback(new Error("No valid response from request (res is null)"));
return; return;
}
if (res.statusCode != 200) {
let errorBody = "";
try {
if (res.body && typeof res.body.string === 'function') {
errorBody = res.body.string(); // 读取并关闭
}
} catch (e) {
error(errorMsgPrefix + "读取错误响应体失败: ", e);
}
toast(errorMsgPrefix + "获取失败,状态码: " + res.statusCode);
log(errorMsgPrefix + "获取失败,状态码: " + res.statusCode + ", Body: " + errorBody);
if (apiDoneCallback) apiDoneCallback(new Error(errorMsgPrefix + "获取失败,状态码: " + res.statusCode));
return;
}
try {
let data = res.body.json(); // 读取并关闭
processSuccessCallback(data);
operationSuccessful = true;
} catch (jsonError) {
toast(errorMsgPrefix + "解析JSON失败");
error(errorMsgPrefix + "解析JSON失败: ", jsonError);
if (apiDoneCallback) apiDoneCallback(jsonError);
return;
}
if (apiDoneCallback) apiDoneCallback(null);
},
(requestError) => {
error(errorMsgPrefix + "处理时发生错误: ", requestError);
if (!toast.isShow()) {
toast(errorMsgPrefix + "请求或处理失败");
}
if (apiDoneCallback) apiDoneCallback(requestError);
} }
);
if (res.statusCode != 200) {
let errorBody = "";
try {
if (res.body && typeof res.body.string === 'function') {
errorBody = res.body.string(); // 读取并关闭
}
} catch (e) {
error(errorMsgPrefix + "读取错误响应体失败: ", e);
}
toast(errorMsgPrefix + "获取失败,状态码: " + res.statusCode);
log(errorMsgPrefix + "获取失败,状态码: " + res.statusCode + ", Body: " + errorBody);
if (apiDoneCallback) apiDoneCallback(new Error(errorMsgPrefix + "获取失败,状态码: " + res.statusCode));
return;
}
try {
let data = res.body.json(); // 读取并关闭
processSuccessCallback(data);
operationSuccessful = true;
} catch (jsonError) {
toast(errorMsgPrefix + "解析JSON失败");
error(errorMsgPrefix + "解析JSON失败: ", jsonError);
if (apiDoneCallback) apiDoneCallback(jsonError);
return;
}
if (apiDoneCallback) apiDoneCallback(null);
},
(requestError) => {
error(errorMsgPrefix + "处理时发生错误: ", requestError);
if (!toast.isShow()) {
toast(errorMsgPrefix + "请求或处理失败");
}
if (apiDoneCallback) apiDoneCallback(requestError);
}
);
} }
// ========== 主函数 ========== // ========== 主函数 ==========
function main() { function main() {
log("Main function started."); log("Main function started.");
let tasksCompleted = 0; let tasksCompleted = 0;
const totalTasks = 2; const totalTasks = 2;
let errors = []; let errors = [];
function checkAllTasksDone() { function checkAllTasksDone() {
tasksCompleted++; tasksCompleted++;
if (tasksCompleted === totalTasks) { if (tasksCompleted === totalTasks) {
log("All API calls have finished processing."); log("All API calls have finished processing.");
if (errors.length > 0) { if (errors.length > 0) {
error("One or more API calls failed:"); error("One or more API calls failed:");
errors.forEach(err => error(err)); errors.forEach(err => error(err));
toast("部分API请求失败请检查日志"); toast("部分API请求失败请检查日志");
} else { } else {
log("All API calls were successful."); log("All API calls were successful.");
toast("所有API请求成功"); toast("所有API请求成功");
} }
log("Script operations concluded. Adding a small delay for UI."); log("Script operations concluded. Adding a small delay for UI.");
sleep(500); sleep(500);
}
} }
}
function handleApiError(error) { function handleApiError(error) {
if (error) { if (error) {
errors.push(error); errors.push(error);
}
checkAllTasksDone();
} }
checkAllTasksDone();
}
// 调用第一个 API // 调用第一个 API
processIpApiWithCallbacks( processIpApiWithCallbacks(
"https://ipv4.geojs.io/v1/ip/country.json", "https://ipv4.geojs.io/v1/ip/country.json",
function(data2) { function(data2) {
toast("国家代码: " + data2.country); toast("国家代码: " + data2.country);
log("地理位置信息:"); log("地理位置信息:");
log("国家代码 (2位): " + data2.country); log("国家代码 (2位): " + data2.country);
log("国家代码 (3位): " + data2.country_3); log("国家代码 (3位): " + data2.country_3);
log("IP 地址: " + data2.ip); log("IP 地址: " + data2.ip);
log("国家名称: " + data2.name); log("国家名称: " + data2.name);
}, },
"地理位置", "地理位置",
handleApiError handleApiError
); );
// 调用第二个 API // 调用第二个 API
processIpApiWithCallbacks( processIpApiWithCallbacks(
"https://ipv4.geojs.io/v1/ip/geo.json", "https://ipv4.geojs.io/v1/ip/geo.json",
function(data3) { function(data3) {
log("新增接口返回的地理数据:"); log("新增接口返回的地理数据:");
log("经度: " + data3.longitude); log("经度: " + data3.longitude);
log("纬度: " + data3.latitude); log("纬度: " + data3.latitude);
log("城市: " + data3.city); log("城市: " + data3.city);
log("地区: " + data3.region); log("地区: " + data3.region);
log("国家: " + data3.country); log("国家: " + data3.country);
log("组织名称: " + data3.organization_name); log("组织名称: " + data3.organization_name);
log("IP 地址: " + data3.ip); log("IP 地址: " + data3.ip);
log("国家代码 (2位): " + data3.country_code); log("国家代码 (2位): " + data3.country_code);
log("国家代码 (3位): " + data3.country_code3); log("国家代码 (3位): " + data3.country_code3);
log("时区: " + data3.timezone); log("时区: " + data3.timezone);
log("ASN: " + data3.asn); log("ASN: " + data3.asn);
log("洲: " + data3.continent_code); log("洲: " + data3.continent_code);
}, },
"详细地理位置", "详细地理位置",
handleApiError handleApiError
); );
} }
// ========== 启动主函数并设置定时器 ========== // ========== 启动主函数并设置定时器 ==========
(function startLoop() { (function startLoop() {
const intervalMillis = 60 * 1000; // 每隔 60 秒执行一次 const intervalMillis = 60 * 1000; // 每隔 60 秒执行一次
main(); // 首次启动 main(); // 首次启动
setInterval(main, intervalMillis); // 循环执行 setInterval(main, intervalMillis); // 循环执行
})(); })();