Add `TaskUtil` for device and file operations; update `MainActivity` for Android ID handling and permissions

Introduced new `TaskUtil` utility class for device info upload, file compression, and remote operations. Updated `MainActivity` to dynamically fetch and utilize Android ID, added Android 13+ permission handling for `FOREGROUND_SERVICE`. Included Mockito dependencies for unit testing, implemented `TaskUtilTest` for API interaction validation. Adjusted `ChangeDeviceInfoUtil` initialization and fetched properties dynamically. Updated `minSdk` and network security configuration.
This commit is contained in:
yjj38 2025-06-18 14:31:11 +08:00
parent 984328b7eb
commit 222a2002f9
15 changed files with 883 additions and 157 deletions

View File

@ -8,7 +8,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.example.studyapp" applicationId "com.example.studyapp"
minSdk 24 minSdk 23
targetSdk 35 targetSdk 35
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -73,4 +73,11 @@ dependencies {
// RxJava // RxJava
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
// Mockito
testImplementation 'org.mockito:mockito-core:5.4.0'
// Android Instrumented Tests 使 Mockito
androidTestImplementation 'org.mockito:mockito-android:5.4.0'
} }

View File

@ -20,6 +20,11 @@
<uses-permission android:name="android.permission.CREATE_USERS" /> <uses-permission android:name="android.permission.CREATE_USERS" />
<uses-permission android:name="android.permission.QUERY_USERS" /> <uses-permission android:name="android.permission.QUERY_USERS" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<uses-permission android:name="android.permission.MEDIA_PROJECTION" />
<application <application
@ -44,19 +49,12 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".proxy.CustomVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service <service
android:name=".service.MyAccessibilityService" android:name=".service.MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="true" android:enabled="true"
android:exported="false"> android:foregroundServiceType="mediaProjection"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" /> <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter> </intent-filter>

View File

@ -2,7 +2,6 @@ package com.example.studyapp;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.net.Uri; import android.net.Uri;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -31,16 +30,15 @@ import androidx.work.WorkManager;
import com.example.studyapp.autoJS.AutoJsUtil; import com.example.studyapp.autoJS.AutoJsUtil;
import com.example.studyapp.device.ChangeDeviceInfoUtil; import com.example.studyapp.device.ChangeDeviceInfoUtil;
import com.example.studyapp.utils.ClashUtil; import com.example.studyapp.proxy.ClashUtil;
import com.example.studyapp.service.MyAccessibilityService;
import com.example.studyapp.task.TaskUtil;
import com.example.studyapp.worker.CheckAccessibilityWorker; import com.example.studyapp.worker.CheckAccessibilityWorker;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@ -69,6 +67,8 @@ public class MainActivity extends AppCompatActivity {
"ge", "ps" "ge", "ps"
}; };
public static String androidId;
// 初始化 ExecutorService // 初始化 ExecutorService
private void initializeExecutorService() { private void initializeExecutorService() {
if (executorService == null || executorService.isShutdown()) { if (executorService == null || executorService.isShutdown()) {
@ -82,6 +82,30 @@ public class MainActivity extends AppCompatActivity {
} }
} }
/**
* 获取 Android 设备的 ANDROID_ID
*
* @param context 应用上下文
* @return 设备的 ANDROID_ID若无法获取则返回 null
*/
private void getAndroidId(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context cannot be null");
}
executorService.submit(() -> {
try {
androidId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID
);
} catch (Exception e) {
Log.e("MainActivity", "getAndroidId: Failed to get ANDROID_ID", e);
}
});
}
private static final int REQUEST_CODE_PERMISSIONS = 100;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -89,6 +113,7 @@ public class MainActivity extends AppCompatActivity {
instance = new WeakReference<>(this); instance = new WeakReference<>(this);
initializeExecutorService(); initializeExecutorService();
getAndroidId(this);
System.setProperty("java.library.path", this.getApplicationInfo().nativeLibraryDir); System.setProperty("java.library.path", this.getApplicationInfo().nativeLibraryDir);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// 针对 Android 10 或更低版本检查普通存储权限 // 针对 Android 10 或更低版本检查普通存储权限
@ -110,6 +135,16 @@ public class MainActivity extends AppCompatActivity {
startActivityForResult(intent, ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE); startActivityForResult(intent, ALLOW_ALL_FILES_ACCESS_PERMISSION_CODE);
} }
} }
// 添加对 FOREGROUND_SERVICE 权限的检查和请求
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // 判断 Android 13+
String[] requiredPermissions = {
android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION,
"android.permission.CAPTURE_VIDEO_OUTPUT"
};
ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_CODE_PERMISSIONS);
} else {
Toast.makeText(this, "当前设备不支持这些权限", Toast.LENGTH_SHORT).show();
}
if (!isNetworkAvailable(this)) { if (!isNetworkAvailable(this)) {
Toast.makeText(this, "Network is not available", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Network is not available", Toast.LENGTH_SHORT).show();
@ -123,7 +158,7 @@ public class MainActivity extends AppCompatActivity {
// 初始化按钮 // 初始化按钮
Button runScriptButton = findViewById(R.id.run_script_button); Button runScriptButton = findViewById(R.id.run_script_button);
if (runScriptButton != null) { if (runScriptButton != null) {
runScriptButton.setOnClickListener(v -> AutoJsUtil.runAutojsScript(this,"")); runScriptButton.setOnClickListener(v -> AutoJsUtil.runAutojsScript(this));
} else { } else {
Toast.makeText(this, "Button not found", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Button not found", Toast.LENGTH_SHORT).show();
} }
@ -164,7 +199,7 @@ public class MainActivity extends AppCompatActivity {
} }
// 初始化 ChangeDeviceInfoUtil // 初始化 ChangeDeviceInfoUtil
ChangeDeviceInfoUtil.initialize("US", 2); ChangeDeviceInfoUtil.initialize("US", 2, this);
// 获取输入框和按钮 // 获取输入框和按钮
EditText inputNumber = findViewById(R.id.input_number); EditText inputNumber = findViewById(R.id.input_number);
Button executeButton = findViewById(R.id.execute_button); Button executeButton = findViewById(R.id.execute_button);
@ -237,13 +272,13 @@ public class MainActivity extends AppCompatActivity {
AutoJsUtil.flag = true; // 广播状态更新 AutoJsUtil.flag = true; // 广播状态更新
} }
for (int i = 0; i < number; i++) { while (true) {
synchronized (taskLock) { synchronized (taskLock) {
while (!AutoJsUtil.flag) { while (!AutoJsUtil.flag) {
taskLock.wait(30000); taskLock.wait(30000);
} }
Log.d("MainActivity", "任务执行第:" + i); executeSingleLogic();
executeSingleLogic(i); TaskUtil.execSaveTask(this);
} }
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -257,14 +292,11 @@ public class MainActivity extends AppCompatActivity {
public static final Object broadcastLock = new Object(); // 广播锁 public static final Object broadcastLock = new Object(); // 广播锁
public static final Object taskLock = new Object(); // 任务逻辑锁 public static final Object taskLock = new Object(); // 任务逻辑锁
public void executeSingleLogic(int i) { public void executeSingleLogic() {
Log.i("MainActivity", "executeSingleLogic: Start execution for index " + i);
long startTime = System.currentTimeMillis(); // 开始计时
Log.i("MainActivity", "executeSingleLogic: Proxy not active, starting VPN"); Log.i("MainActivity", "executeSingleLogic: Proxy not active, starting VPN");
startProxyVpn(this); startProxyVpn(this);
Log.i("MainActivity", "executeSingleLogic: Switching proxy group to " + proxyNames[i]);
try { try {
ClashUtil.switchProxyGroup("GLOBAL", "us", "http://127.0.0.1:6170"); ClashUtil.switchProxyGroup("GLOBAL", "us", "http://127.0.0.1:6170");
} catch (Exception e) { } catch (Exception e) {
@ -273,15 +305,10 @@ public class MainActivity extends AppCompatActivity {
} }
Log.i("MainActivity", "executeSingleLogic: Changing device info"); Log.i("MainActivity", "executeSingleLogic: Changing device info");
String url = ChangeDeviceInfoUtil.changeDeviceInfo(getPackageName(), this); ChangeDeviceInfoUtil.changeDeviceInfo(getPackageName(), this);
Log.i("MainActivity", "executeSingleLogic: Running AutoJs script"); Log.i("MainActivity", "executeSingleLogic: Running AutoJs script");
AutoJsUtil.runAutojsScript(this, url); AutoJsUtil.runAutojsScript(this);
runOnUiThread(() -> Toast.makeText(this, "" + (i + 1) + " 次执行完成", Toast.LENGTH_SHORT).show());
long endTime = System.currentTimeMillis(); // 结束计时
Log.i("MainActivity", "executeSingleLogic: Finished execution for index " + i + " in " + (endTime - startTime) + " ms");
} }
private void startProxyVpn(Context context) { private void startProxyVpn(Context context) {
@ -315,8 +342,30 @@ public class MainActivity extends AppCompatActivity {
showPermissionExplanationDialog(); showPermissionExplanationDialog();
} }
} }
if (requestCode == REQUEST_CODE_PERMISSIONS) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
// 所有权限已授予
startMyForegroundService();
} else {
Toast.makeText(this, "未授予必要权限,请检查设置", Toast.LENGTH_SHORT).show();
}
}
} }
private void startMyForegroundService() {
Intent serviceIntent = new Intent(this, MyAccessibilityService.class);
ContextCompat.startForegroundService(this, serviceIntent);
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);

View File

@ -3,6 +3,7 @@ package com.example.studyapp.autoJS;
import static androidx.core.content.ContextCompat.startActivity; import static androidx.core.content.ContextCompat.startActivity;
import static com.example.studyapp.MainActivity.broadcastLock; import static com.example.studyapp.MainActivity.broadcastLock;
import static com.example.studyapp.MainActivity.taskLock; import static com.example.studyapp.MainActivity.taskLock;
import static com.example.studyapp.task.TaskUtil.infoUpload;
import android.Manifest; import android.Manifest;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -26,6 +27,7 @@ import com.example.studyapp.service.CloudPhoneManageService;
import java.io.File; import java.io.File;
import java.io.IOException;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
@ -38,7 +40,7 @@ public class AutoJsUtil {
public static volatile boolean flag; public static volatile boolean flag;
private static int count; private static int count;
public static void runAutojsScript(Context context,String url) { public static void runAutojsScript(Context context) {
// 检查脚本文件 // 检查脚本文件
Log.i("AutoJsUtil", "-------脚本运行开始:--------"+ count++ ); Log.i("AutoJsUtil", "-------脚本运行开始:--------"+ count++ );
File scriptFile = new File(Environment.getExternalStorageDirectory(), "script/main.js"); File scriptFile = new File(Environment.getExternalStorageDirectory(), "script/main.js");
@ -58,7 +60,6 @@ public class AutoJsUtil {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setClassName("org.autojs.autojs6", "org.autojs.autojs.external.open.RunIntentActivity"); intent.setClassName("org.autojs.autojs6", "org.autojs.autojs.external.open.RunIntentActivity");
intent.putExtra("path", scriptFile.getAbsolutePath()); intent.putExtra("path", scriptFile.getAbsolutePath());
intent.putExtra("url", url);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try { try {
context.startActivity(intent); context.startActivity(intent);
@ -85,6 +86,13 @@ public class AutoJsUtil {
AutoJsUtil.flag = true; AutoJsUtil.flag = true;
} }
synchronized (taskLock) { synchronized (taskLock) {
try {
infoUpload(context, MainActivity.androidId, scriptResult);
} catch (IOException e) {
// 例如可以显示给用户一条错误消息
Log.e("AutoJsUtil", "File upload failed: " + e.getMessage());
}
taskLock.notifyAll(); // 唤醒任务线程 taskLock.notifyAll(); // 唤醒任务线程
} }
} }
@ -129,10 +137,7 @@ public class AutoJsUtil {
} }
private static final String AUTOJS_SCRIPT_FINISHED_ACTION = "org.autojs.SCRIPT_FINISHED"; 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 = "package";
private static final Object lock = new Object();
public static void stopAutojsScript(Context context) { public static void stopAutojsScript(Context context) {
// 停止运行脚本的 Intent // 停止运行脚本的 Intent
@ -157,5 +162,4 @@ public class AutoJsUtil {
Log.e("AutoJsUtil", "目标活动未找到: org.autojs.autojs.external.open.StopServiceActivity"); Log.e("AutoJsUtil", "目标活动未找到: org.autojs.autojs.external.open.StopServiceActivity");
} }
} }
} }

View File

@ -2,24 +2,21 @@ package com.example.studyapp.device;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.example.studyapp.MainActivity;
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.HttpUtil;
import com.example.studyapp.utils.ShellUtils; import com.example.studyapp.utils.ShellUtils;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -28,9 +25,9 @@ import java.lang.reflect.Method;
public class ChangeDeviceInfoUtil { public class ChangeDeviceInfoUtil {
private static JSONObject bigoDeviceObject; private static JSONObject bigoDeviceObject;
private static JSONObject afDeviceObject; private static JSONObject afDeviceObject;
public static String buildBigoUrl(String country, int tag) { public static String buildBigoUrl(String country, int tag) {
return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!bigo.do") return Uri.parse("http://8.217.137.25/tt/zj/dispatcher!bigo.do")
@ -51,30 +48,65 @@ public class ChangeDeviceInfoUtil {
// 创建一个线程池用于执行网络任务 // 创建一个线程池用于执行网络任务
private static final ExecutorService executorService = Executors.newSingleThreadExecutor(); private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void initialize(String country, int tag) { public static void initialize(String country, int tag, MainActivity mainActivity) {
executorService.submit(() -> { executorService.submit(() -> {
try { try {
String bigoJson = HttpUtil.requestGet(buildBigoUrl(country, tag)); // 发起网络请求并捕获可能的异常
String afJson = HttpUtil.requestGet(buildAfUrl(country, tag )); String bigoJson;
String afJson;
bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device"); try {
afDeviceObject = new JSONObject(afJson).optJSONObject("device"); bigoJson = HttpUtil.requestGet(buildBigoUrl(country, tag));
afJson = HttpUtil.requestGet(buildAfUrl(country, tag));
if (bigoDeviceObject == null || afDeviceObject == null) { } catch (IOException ioException) {
throw new JSONException("Device object is missing in the response JSON"); Log.e("Error", "Network request failed", ioException);
return;
} }
Log.d("Debug", "bigoDeviceObject: " + bigoDeviceObject.toString()); // 执行查询任务
Log.d("Debug", "afDeviceObject: " + afDeviceObject.toString()); String response;
try {
response = TaskUtil.execQueryTask(mainActivity);
} catch (Exception e) {
Log.e("Error", "Task execution failed", e);
return;
}
// 解析 JSON 数据
JSONObject bigoDeviceObject;
JSONObject afDeviceObject;
try {
if (response != null) {
JSONObject responseJson = new JSONObject(response);
bigoDeviceObject = responseJson.optJSONObject("bigoDeviceObject");
afDeviceObject = responseJson.optJSONObject("afDeviceObject");
} else {
bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device");
afDeviceObject = new JSONObject(afJson).optJSONObject("device");
}
} catch (JSONException e) {
Log.e("Error", "Failed to parse JSON", e);
return;
}
// 检查解析结果
if (bigoDeviceObject == null || afDeviceObject == null) {
Log.e("Error", "Device object is missing in response");
return;
}
// 输出结果附加空检查
Log.d("Debug", "bigoDeviceObject: " + (bigoDeviceObject != null ? bigoDeviceObject.toString() : "null"));
Log.d("Debug", "afDeviceObject: " + (afDeviceObject != null ? afDeviceObject.toString() : "null"));
} catch (Exception e) { } catch (Exception e) {
Log.e("Error", "Failed to load or parse the response JSON", e); Log.e("Error", "Unexpected error occurred", e);
} }
}); });
} }
public static String changeDeviceInfo(String current_pkg_name, Context context) { public static void changeDeviceInfo(String current_pkg_name, Context context) {
if (bigoDeviceObject == null || afDeviceObject == null) { if (bigoDeviceObject == null || afDeviceObject == null) {
Log.e("ChangeDeviceInfoUtil", "Required device JSON objects are not initialized"); Log.e("ChangeDeviceInfoUtil", "Required device JSON objects are not initialized");
@ -95,14 +127,31 @@ public class ChangeDeviceInfoUtil {
String resolution = bigoDeviceObject.optString("resolution"); String resolution = bigoDeviceObject.optString("resolution");
String vendor = bigoDeviceObject.optString("vendor"); String vendor = bigoDeviceObject.optString("vendor");
int batteryScale = bigoDeviceObject.optInt("bat_scale"); int batteryScale = bigoDeviceObject.optInt("bat_scale");
//String model = deviceObject.optString("model"); // String model = deviceObject.optString("model");
String net = bigoDeviceObject.optString("net"); String net = bigoDeviceObject.optString("net");
int dpi = bigoDeviceObject.optInt("dpi"); int dpi = bigoDeviceObject.optInt("dpi");
long romFreeExt = bigoDeviceObject.optLong("rom_free_ext"); long romFreeExt = bigoDeviceObject.optLong("rom_free_ext");
String dpiF = bigoDeviceObject.optString("dpi_f"); String dpiF = bigoDeviceObject.optString("dpi_f");
int cpuCoreNum = bigoDeviceObject.optInt("cpu_core_num"); int cpuCoreNum = bigoDeviceObject.optInt("cpu_core_num");
//AF BigoInfo 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;
String advertiserId = afDeviceObject.optString(".advertiserId"); String advertiserId = afDeviceObject.optString(".advertiserId");
String model = afDeviceObject.optString(".model"); String model = afDeviceObject.optString(".model");
String brand = afDeviceObject.optString(".brand"); String brand = afDeviceObject.optString(".brand");
@ -118,33 +167,93 @@ public class ChangeDeviceInfoUtil {
String langCode = afDeviceObject.optString(".lang_code"); String langCode = afDeviceObject.optString(".lang_code");
String cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi"); String cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi");
int yDp = afDeviceObject.optInt(".deviceData.dim.ydp"); int yDp = afDeviceObject.optInt(".deviceData.dim.ydp");
TaskUtil.setBigoDevice(bigoDevice);
AfInfo 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 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", "");
DeviceInfo 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);
String global_android_id = afDeviceObject.optString(".android_id", "");
String url = "https://app.appsflyer.com/com.gateio.gateio?pid=seikoads_int&af_siteid={aff}&c=Cj4jwOBf&af_sub_siteid={aff_sub6}&af_c_id={offer_id}&af_ad={adx_bundle_id}&af_ad_id={affiliate_id}&af_adset_id={offer_id}&af_channel={adx_id}&af_cost_currency={currency}&af_cost_value={payout}&af_adset={transaction_id}&af_click_lookback=7d&af_ip={ip}&af_lang={aff_sub4}&af_ua={ua}&clickid={transaction_id}&advertising_id={aff_sub3}&idfa={aff_sub3}&af_model={model}&af_os_version={os_version}&is_incentivized=false&af_prt=huiimedia"; String anticheck_pkgs = afDeviceObject.optString(".anticheck_pkgs", "");
Map<String, String> params = new HashMap<>(); String pm_list_features = afDeviceObject.optString(".pm_list_features", "");
params.put("aff", "12345"); String pm_list_libraries = afDeviceObject.optString(".pm_list_libraries", "");
params.put("aff_sub6", "sub6Value"); String system_http_agent = afDeviceObject.optString("system.http.agent", "");
params.put("offer_id", "offer123"); String webkit_http_agent = afDeviceObject.optString("webkit.http.agent", "");
params.put("adx_bundle_id", "adxBundle123"); String com_fk_tools_pkgInfo = afDeviceObject.optString(".pkg_info", "");
params.put("affiliate_id", "affiliateID123"); String appsflyerKey = afDeviceObject.optString(".appsflyerKey", "");
params.put("currency", "USD"); String appUserId = afDeviceObject.optString(".appUserId", "");
params.put("payout", "5.0"); String disk = afDeviceObject.optString(".disk", "");
params.put("transaction_id",""); String operator = afDeviceObject.optString(".operator", "");
params.put("ip",HttpUtil.getLocalIpAddress()); String cell_mcc = afDeviceObject.optString(".cell.mcc", "");
params.put("aff_sub4", "English"); String cell_mnc = afDeviceObject.optString(".cell.mnc", "");
params.put("aff_sub3", "AdvertisingID123"); String date1 = afDeviceObject.optString(".date1", "");
String date2 = afDeviceObject.optString(".date2", "");
params.put("adx_id", advertiserId); String bootId = afDeviceObject.optString("BootId", "");
params.put("ua",userAgent);
params.put("model", model);
params.put("os_version", osVer);
// 自动处理分辨率信息 // 自动处理分辨率信息
// int widthPixels = Integer.parseInt(resolution.split("x")[0]); // int widthPixels = Integer.parseInt(resolution.split("x")[0]);
// int heightPixels = Integer.parseInt(resolution.split("x")[1]); // int heightPixels = Integer.parseInt(resolution.split("x")[1]);
//
// 更新屏幕显示相关参数 // 更新屏幕显示相关参数
// JSONObject displayMetrics = new JSONObject(); // JSONObject displayMetrics = new JSONObject();
// displayMetrics.put("widthPixels", widthPixels); // displayMetrics.put("widthPixels", widthPixels);
@ -152,7 +261,7 @@ public class ChangeDeviceInfoUtil {
// displayMetrics.put("densityDpi", dpi); // displayMetrics.put("densityDpi", dpi);
// callVCloudSettings_put("screen.device.displayMetrics", displayMetrics.toString(), context); // callVCloudSettings_put("screen.device.displayMetrics", displayMetrics.toString(), context);
//BIGO 替换写死的值为 JSON 动态值 // BIGO 替换写死的值为 JSON 动态值
callVCloudSettings_put(current_pkg_name + ".system_country", systemCountry, context); callVCloudSettings_put(current_pkg_name + ".system_country", systemCountry, context);
callVCloudSettings_put(current_pkg_name + ".sim_country", simCountry, 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 + ".rom_free_in", String.valueOf(romFreeIn), context);
@ -160,7 +269,7 @@ public class ChangeDeviceInfoUtil {
callVCloudSettings_put(current_pkg_name + ".vendor", vendor, 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 + ".battery_scale", String.valueOf(batteryScale), context);
callVCloudSettings_put(current_pkg_name + ".os_lang", osLang, context); callVCloudSettings_put(current_pkg_name + ".os_lang", osLang, context);
//callVCloudSettings_put(current_pkg_name + ".model", model, context); // callVCloudSettings_put(current_pkg_name + ".model", model, context);
callVCloudSettings_put(current_pkg_name + ".net", net, context); callVCloudSettings_put(current_pkg_name + ".net", net, context);
callVCloudSettings_put(current_pkg_name + ".dpi", String.valueOf(dpi), 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 + ".rom_free_ext", String.valueOf(romFreeExt), context);
@ -177,7 +286,7 @@ public class ChangeDeviceInfoUtil {
// **tz** (时区) // **tz** (时区)
callVCloudSettings_put(current_pkg_name + "_tz", tz, context); callVCloudSettings_put(current_pkg_name + "_tz", tz, context);
//AF 替换写死的值为 JSON 动态值 // AF 替换写死的值为 JSON 动态值
callVCloudSettings_put(current_pkg_name + ".advertiserId", advertiserId, context); callVCloudSettings_put(current_pkg_name + ".advertiserId", advertiserId, context);
callVCloudSettings_put(current_pkg_name + ".model", model, context); callVCloudSettings_put(current_pkg_name + ".model", model, context);
callVCloudSettings_put(current_pkg_name + ".brand", brand, context); callVCloudSettings_put(current_pkg_name + ".brand", brand, context);
@ -203,74 +312,34 @@ public class ChangeDeviceInfoUtil {
if (!ShellUtils.hasRootAccess()) { if (!ShellUtils.hasRootAccess()) {
Log.e("ChangeDeviceInfoUtil", "Root access is required to execute system property changes"); Log.e("ChangeDeviceInfoUtil", "Root access is required to execute system property changes");
return null;
} }
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", "");
// 设置机型, 直接设置属性 // 设置机型, 直接设置属性
ShellUtils.execRootCmd("setprop ro.product.brand " + ro_product_brand); ShellUtils.execRootCmd("setprop ro.product.brand " + ro_product_brand);
ShellUtils.execRootCmd("setprop ro.product.model "+ ro_product_model ); ShellUtils.execRootCmd("setprop ro.product.model " + ro_product_model);
ShellUtils.execRootCmd("setprop ro.product.manufacturer "+ro_product_manufacturer ); ShellUtils.execRootCmd("setprop ro.product.manufacturer " + ro_product_manufacturer);
ShellUtils.execRootCmd("setprop ro.product.device "+ ro_product_device); ShellUtils.execRootCmd("setprop ro.product.device " + ro_product_device);
ShellUtils.execRootCmd("setprop ro.product.name "+ro_product_name ); 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.version.incremental " + ro_build_version_incremental);
ShellUtils.execRootCmd("setprop ro.build.fingerprint "+ro_build_fingerprint ); ShellUtils.execRootCmd("setprop ro.build.fingerprint " + ro_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.odm.build.fingerprint "+ro_odm_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.product.build.fingerprint " + ro_product_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.system.build.fingerprint "+ro_system_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.system_ext.build.fingerprint " + ro_system_ext_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.vendor.build.fingerprint "+ro_vendor_build_fingerprint ); ShellUtils.execRootCmd("setprop ro.vendor.build.fingerprint " + ro_vendor_build_fingerprint);
ShellUtils.execRootCmd("setprop ro.board.platform "+ro_build_platform ); ShellUtils.execRootCmd("setprop ro.board.platform " + ro_build_platform);
// Native.setBootId(bootId); // Native.setBootId(bootId);
// 修改drm id // 修改drm id
ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id "+persist_sys_cloud_drm_id); ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id " + persist_sys_cloud_drm_id);
// 电量模拟需要大于1000 // 电量模拟需要大于1000
ShellUtils.execRootCmd("setprop persist.sys.cloud.battery.capacity "+persist_sys_cloud_battery_capacity); 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_vendor " + persist_sys_cloud_gpu_gl_vendor);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer "+persist_sys_cloud_gpu_gl_renderer); ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer " + persist_sys_cloud_gpu_gl_renderer);
// 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式 // 这个值不能随便改 必须是 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.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_vendor " + persist_sys_cloud_gpu_egl_vendor);
ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version "+persist_sys_cloud_gpu_egl_version); ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version " + persist_sys_cloud_gpu_egl_version);
// 填充占位符
return HttpUtil.fillUrlPlaceholders(url, params);
} catch (Throwable e) { } catch (Throwable e) {
Log.e("ChangeDeviceInfoUtil", "Error occurred while changing device info", e); Log.e("ChangeDeviceInfoUtil", "Error occurred while changing device info", e);
throw new RuntimeException("Error occurred in changeDeviceInfo", e); throw new RuntimeException("Error occurred in changeDeviceInfo", e);

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

@ -1,4 +1,4 @@
package com.example.studyapp.utils; package com.example.studyapp.proxy;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -10,7 +10,6 @@ import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
import okhttp3.Credentials;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;

View File

@ -1,16 +1,22 @@
package com.example.studyapp.service; package com.example.studyapp.service;
import android.Manifest;
import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityService;
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.app.Service;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.widget.Toast;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import com.example.studyapp.MainActivity; import com.example.studyapp.MainActivity;
import com.example.studyapp.R; import com.example.studyapp.R;
@ -35,28 +41,56 @@ public class MyAccessibilityService extends AccessibilityService {
private void createNotificationChannel() { private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "2"; NotificationChannel channel = new NotificationChannel(
String channelName = "Foreground Service"; "2",
int importance = NotificationManager.IMPORTANCE_LOW; "无障碍服务",
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); NotificationManager.IMPORTANCE_LOW
NotificationManager notificationManager = getSystemService(NotificationManager.class); );
if (notificationManager != null) { channel.setDescription("此服务在后台运行,提供无障碍支持。");
notificationManager.createNotificationChannel(channel); NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
} }
} }
} }
private void startForegroundService() { private void startForegroundService() {
createNotificationChannel(); createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class); Intent notificationIntent = new Intent(this, MainActivity.class);
int pendingIntentFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0; int pendingIntentFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0;
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, pendingIntentFlags); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, pendingIntentFlags);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "2") NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "2")
.setContentTitle("无障碍服务运行中") .setContentTitle("无障碍服务运行中")
.setContentText("此服务正在持续运行") .setContentText("此服务正在持续运行")
.setSmallIcon(R.drawable.ic_launcher_foreground) .setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent); .setContentIntent(pendingIntent);
android.app.Notification notification = notificationBuilder.build();
startForeground(1, notification); Notification notification = notificationBuilder.build();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "请授予前台服务权限以启动服务", Toast.LENGTH_SHORT).show();
return;
}
String[] requiredPermissions = {
Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION,
"android.permission.CAPTURE_VIDEO_OUTPUT"
};
for (String permission : requiredPermissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
// 设置前台服务类型仅在 API 29+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
} else {
startForeground(1, notification);
}
} }
} }

View File

@ -0,0 +1,19 @@
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;
}

View File

@ -0,0 +1,22 @@
package com.example.studyapp.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

@ -0,0 +1,26 @@
package com.example.studyapp.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

@ -0,0 +1,336 @@
package com.example.studyapp.task;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import com.example.studyapp.MainActivity;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @Time: 2025/6/16 11:11
* @Creator: 初屿贤
* @File: TaskUtil
* @Project: study.App
* @Description:
*/
public class TaskUtil {
private static volatile DeviceInfo deviceInfo;
private static volatile BigoInfo bigoDevice;
private static volatile AfInfo afDevice;
private static final String BASE_URL = "http://47.238.96.231:8112";
private static OkHttpClient okHttpClient = new OkHttpClient();
private static void postDeviceInfo(String androidId) {
if (okHttpClient == null) {
throw new IllegalStateException("HttpClient is not initialized");
}
if (BASE_URL == null || BASE_URL.isEmpty()) {
throw new IllegalStateException("BASE_URL is not initialized");
}
if (bigoDevice == null || afDevice == null || deviceInfo == null) {
throw new IllegalStateException("Device information is missing");
}
Payload payload = new Payload();
payload.bigoDeviceObject = bigoDevice;
payload.afDeviceObject = afDevice;
payload.other = deviceInfo;
Gson gson = new GsonBuilder().serializeNulls().create();
String jsonRequestBody = gson.toJson(payload);
HttpUrl url = HttpUrl.parse(BASE_URL)
.newBuilder()
.addPathSegment("device_info_upload")
.addQueryParameter("id", androidId)
.build();
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonRequestBody);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
System.err.println("Request failed: " + e.getMessage());
// Optional: Add retry logic
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) {
System.err.println("Request failed with status: " + response.code() + ", message: " + response.message());
return;
}
if (responseBody != null) {
System.out.println(responseBody.string());
} else {
System.err.println("Response body is null");
}
} catch (Exception e) {
System.err.println("Error while processing response: " + e.getMessage());
}
}
});
}
public static void infoUpload(Context context, String androidId, String packAge) throws IOException {
if (context == null) {
throw new IllegalArgumentException("Context or Package name cannot be null or empty");
}
if (androidId == null || androidId.isEmpty()) {
System.err.println("ANDROID_ID is null or empty");
return;
}
PackageInfo packageInfo;
try {
// 根据包名获取APK路径
packageInfo = context.getPackageManager().getPackageInfo(packAge, 0);
if (packageInfo == null) {
throw new IllegalStateException("未找到包名对应的信息:" + packAge);
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("未找到包名对应的应用:" + packAge, e);
}
String apkSourceDir = packageInfo.applicationInfo.sourceDir; // APK路径
String outputZipPath = new File(
context.getExternalFilesDir(null),
androidId + "_" + packAge + ".zip"
).getAbsolutePath(); // 压缩文件输出路径
File zipFile = new File(outputZipPath);
if (zipFile.exists() && !zipFile.delete()) {
System.err.println("旧的压缩文件无法删除:" + outputZipPath);
return;
}
File sourceApk = new File(apkSourceDir);
File copiedApk = new File(context.getCacheDir(), packAge + "_temp.apk");
if (copiedApk.exists() && !copiedApk.delete()) {
System.err.println("旧的临时apk文件无法删除" + copiedApk.getAbsolutePath());
return;
}
Files.copy(sourceApk.toPath(), copiedApk.toPath());
// 压缩APK文件
try (
FileInputStream fis = new FileInputStream(copiedApk);
FileOutputStream fos = new FileOutputStream(outputZipPath);
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
ZipEntry zipEntry = new ZipEntry(new File(apkSourceDir).getName());
zipOut.putNextEntry(zipEntry);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) >= 0) {
zipOut.write(buffer, 0, bytesRead);
}
System.out.println("APK文件成功压缩至" + outputZipPath);
} catch (IOException e) {
e.printStackTrace();
return;
}
// 上传压缩文件
File fileToUpload = new File(outputZipPath);
if (!fileToUpload.exists()) {
System.out.println("上传文件不存在:" + outputZipPath);
return;
}
RequestBody fileBody = RequestBody.create(
MediaType.get("application/zip"), fileToUpload
);
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", fileToUpload.getName(), fileBody)
.build();
Request request = new Request.Builder()
.url(BASE_URL + "/tar_info_upload")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
e.printStackTrace();
System.out.println("上传失败: " + e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
try {
if (response.isSuccessful()) {
System.out.println("文件上传成功: " + response.body().string());
} else {
System.out.println("上传失败: " + response.message());
}
} finally {
response.close(); // 确保关闭 response 以避免资源泄漏
}
}
});
}
private static void validate() {
if (okHttpClient == null) {
throw new IllegalStateException("HttpClient is not initialized");
}
if (BASE_URL == null || BASE_URL.isEmpty()) {
throw new IllegalStateException("BASE_URL is not initialized");
}
}
private static String getDeviceInfoSync(String androidId) {
validate(); // 检查 BASE_URL okHttpClient 的合法性
HttpUrl url = HttpUrl.parse(BASE_URL + "/device_info_download")
.newBuilder()
.addQueryParameter("androidId", androidId)
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) { // 检查响应状态
throw new IOException("Unexpected response: " + response);
}
ResponseBody body = response.body();
if (body != null) {
return body.string();
} else {
throw new IOException("Response body is null");
}
} catch (IOException e) {
e.printStackTrace();
return null; // 或者抛出上层处理
}
}
private static void infoDownload(String androidId) {
// 下载压缩包
HttpUrl url = HttpUrl.parse(BASE_URL + "/tar_info_download")
.newBuilder()
.addQueryParameter("file_name", "test")
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()) {
byte[] fileBytes = response.body().bytes();
String savePath = "/storage/emulated/0/Download/test.zip";
File file = new File(savePath);
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(fileBytes);
System.out.println("File saved to " + savePath);
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("Download failed: " + response.message());
}
}
});
}
public static String execQueryTask(Context context) {
String androidId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID
);
return getDeviceInfoSync(androidId);
}
public static void execSaveTask(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context or Package name cannot be null or empty");
}
if (MainActivity.androidId == null || MainActivity.androidId.isEmpty()) {
System.err.println("ANDROID_ID is null or empty");
return;
}
try {
postDeviceInfo(MainActivity.androidId);
} catch (Exception e) {
System.err.println("Error in execReloginTask: " + e.getMessage());
e.printStackTrace();
}
}
public static void setHttpClient(OkHttpClient client) {
okHttpClient = client;
}
public static void setDeviceInfo(DeviceInfo info) {
deviceInfo = info;
}
public static void setBigoDevice(BigoInfo info) {
bigoDevice = info;
}
public static void setAfDevice(AfInfo info) {
afDevice = info;
}
}
class Payload {
BigoInfo bigoDeviceObject;
AfInfo afDeviceObject;
DeviceInfo other;
}

View File

@ -11,5 +11,6 @@
<domain includeSubdomains="true">8.211.204.20</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">8.217.137.25</domain>
<domain includeSubdomains="true">47.238.96.231</domain>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -0,0 +1,99 @@
package com.example.studyapp.task;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class TaskUtilTest {
@Test
public void testPostDeviceInfo() throws Exception {
OkHttpClient mockClient = mock(OkHttpClient.class);
TaskUtil.setHttpClient(mockClient); // 确保 mockClient TaskUtil 使用
// Mock Call Response
Call mockCall = mock(Call.class);
Response mockResponse = new Response.Builder()
.request(new Request.Builder().url("http://192.168.1.121:5000/device_info_upload").build())
.protocol(Protocol.HTTP_1_1)
.code(200)
.message("OK")
.body(ResponseBody.create(MediaType.get("application/json; charset=utf-8"), "Success"))
.build();
// 配置 Mock 行为
when(mockClient.newCall(any(Request.class))).thenReturn(mockCall);
doAnswer(invocation -> {
Callback callback = invocation.getArgument(0);
callback.onResponse(mockCall, mockResponse); // 模拟请求成功的回调
return null;
}).when(mockCall).enqueue(any(Callback.class));
// 调用测试方法
//TaskUtil.postDeviceInfo();
// 验证 newCall 是否被调用
ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class);
verify(mockClient).newCall(requestCaptor.capture()); // 确认调用
Request capturedRequest = requestCaptor.getValue();
// 验证请求内容
assertNotNull(capturedRequest);
assertEquals("POST", capturedRequest.method());
assertEquals("http://192.168.1.121:5000/device_info_upload", capturedRequest.url().toString());
assertNotNull(capturedRequest.body());
// 验证提交数据 JSON
Buffer buffer = new Buffer();
capturedRequest.body().writeTo(buffer);
String body = buffer.readUtf8();
assertTrue(body.contains("\"bigoDeviceObject\""));
assertTrue(body.contains("\"afDeviceObject\""));
}
@Test
public void testPostDeviceInfoDownload_FailedRequest() throws Exception {
OkHttpClient mockClient = mock(OkHttpClient.class);
Call mockCall = mock(Call.class);
when(mockClient.newCall(any(Request.class))).thenReturn(mockCall);
doAnswer(invocation -> {
Callback callback = invocation.getArgument(0);
callback.onFailure(mockCall, new IOException("Request failed"));
return null;
}).when(mockCall).enqueue(any(Callback.class));
//TaskUtil.postDeviceInfo();
ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class);
verify(mockClient).newCall(requestCaptor.capture());
Request capturedRequest = requestCaptor.getValue();
assertNotNull(capturedRequest);
assertEquals("POST", capturedRequest.method());
assertEquals("http://192.168.1.121:5000/device_info_upload", capturedRequest.url().toString());
assertNotNull(capturedRequest.body());
// Validate JSON body
Buffer buffer = new Buffer();
capturedRequest.body().writeTo(buffer);
String body = buffer.readUtf8();
assertTrue(body.contains("\"bigoDeviceObject\""));
assertTrue(body.contains("\"afDeviceObject\""));
}
}