diff --git a/app/build.gradle b/app/build.gradle index 3317bad..4ce4220 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { defaultConfig { applicationId "com.example.studyapp" - minSdk 24 + minSdk 23 targetSdk 35 versionCode 1 versionName "1.0" @@ -73,4 +73,11 @@ dependencies { // 如果需要 RxJava 支持(可选) 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' + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ac24cf..df578f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,11 @@ + + + + + - - - - - + android:foregroundServiceType="mediaProjection" + android:exported="true"> diff --git a/app/src/main/java/com/example/studyapp/MainActivity.java b/app/src/main/java/com/example/studyapp/MainActivity.java index 1082467..b7426ef 100644 --- a/app/src/main/java/com/example/studyapp/MainActivity.java +++ b/app/src/main/java/com/example/studyapp/MainActivity.java @@ -2,7 +2,6 @@ package com.example.studyapp; import android.app.Activity; import android.app.AlertDialog; -import android.content.BroadcastReceiver; import android.net.Uri; import android.content.Context; import android.content.Intent; @@ -31,16 +30,15 @@ import androidx.work.WorkManager; import com.example.studyapp.autoJS.AutoJsUtil; 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 java.lang.ref.WeakReference; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; public class MainActivity extends AppCompatActivity { @@ -69,6 +67,8 @@ public class MainActivity extends AppCompatActivity { "ge", "ps" }; + public static String androidId; + // 初始化 ExecutorService private void initializeExecutorService() { 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 protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -89,6 +113,7 @@ public class MainActivity extends AppCompatActivity { instance = new WeakReference<>(this); initializeExecutorService(); + getAndroidId(this); System.setProperty("java.library.path", this.getApplicationInfo().nativeLibraryDir); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { // 针对 Android 10 或更低版本检查普通存储权限 @@ -110,6 +135,16 @@ public class MainActivity extends AppCompatActivity { 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)) { 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); if (runScriptButton != null) { - runScriptButton.setOnClickListener(v -> AutoJsUtil.runAutojsScript(this,"")); + runScriptButton.setOnClickListener(v -> AutoJsUtil.runAutojsScript(this)); } else { Toast.makeText(this, "Button not found", Toast.LENGTH_SHORT).show(); } @@ -164,7 +199,7 @@ public class MainActivity extends AppCompatActivity { } // 初始化 ChangeDeviceInfoUtil - ChangeDeviceInfoUtil.initialize("US", 2); + ChangeDeviceInfoUtil.initialize("US", 2, this); // 获取输入框和按钮 EditText inputNumber = findViewById(R.id.input_number); Button executeButton = findViewById(R.id.execute_button); @@ -237,13 +272,13 @@ public class MainActivity extends AppCompatActivity { AutoJsUtil.flag = true; // 广播状态更新 } - for (int i = 0; i < number; i++) { + while (true) { synchronized (taskLock) { while (!AutoJsUtil.flag) { taskLock.wait(30000); } - Log.d("MainActivity", "任务执行第:" + i); - executeSingleLogic(i); + executeSingleLogic(); + TaskUtil.execSaveTask(this); } } } catch (InterruptedException e) { @@ -257,14 +292,11 @@ public class MainActivity extends AppCompatActivity { public static final Object broadcastLock = new Object(); // 广播锁 public static final Object taskLock = new Object(); // 任务逻辑锁 - public void executeSingleLogic(int i) { - Log.i("MainActivity", "executeSingleLogic: Start execution for index " + i); - long startTime = System.currentTimeMillis(); // 开始计时 + public void executeSingleLogic() { Log.i("MainActivity", "executeSingleLogic: Proxy not active, starting VPN"); startProxyVpn(this); - Log.i("MainActivity", "executeSingleLogic: Switching proxy group to " + proxyNames[i]); try { ClashUtil.switchProxyGroup("GLOBAL", "us", "http://127.0.0.1:6170"); } catch (Exception e) { @@ -273,15 +305,10 @@ public class MainActivity extends AppCompatActivity { } Log.i("MainActivity", "executeSingleLogic: Changing device info"); - String url = ChangeDeviceInfoUtil.changeDeviceInfo(getPackageName(), this); + ChangeDeviceInfoUtil.changeDeviceInfo(getPackageName(), this); Log.i("MainActivity", "executeSingleLogic: Running AutoJs script"); - AutoJsUtil.runAutojsScript(this, url); - - 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"); + AutoJsUtil.runAutojsScript(this); } private void startProxyVpn(Context context) { @@ -315,8 +342,30 @@ public class MainActivity extends AppCompatActivity { 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 protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/java/com/example/studyapp/autoJS/AutoJsUtil.java b/app/src/main/java/com/example/studyapp/autoJS/AutoJsUtil.java index 44e48ea..623d671 100644 --- a/app/src/main/java/com/example/studyapp/autoJS/AutoJsUtil.java +++ b/app/src/main/java/com/example/studyapp/autoJS/AutoJsUtil.java @@ -3,6 +3,7 @@ package com.example.studyapp.autoJS; import static androidx.core.content.ContextCompat.startActivity; import static com.example.studyapp.MainActivity.broadcastLock; import static com.example.studyapp.MainActivity.taskLock; +import static com.example.studyapp.task.TaskUtil.infoUpload; import android.Manifest; import android.content.ActivityNotFoundException; @@ -26,6 +27,7 @@ import com.example.studyapp.service.CloudPhoneManageService; import java.io.File; +import java.io.IOException; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -38,7 +40,7 @@ public class AutoJsUtil { public static volatile boolean flag; private static int count; - public static void runAutojsScript(Context context,String url) { + public static void runAutojsScript(Context context) { // 检查脚本文件 Log.i("AutoJsUtil", "-------脚本运行开始:--------"+ count++ ); File scriptFile = new File(Environment.getExternalStorageDirectory(), "script/main.js"); @@ -58,7 +60,6 @@ public class AutoJsUtil { Intent intent = new Intent(); intent.setClassName("org.autojs.autojs6", "org.autojs.autojs.external.open.RunIntentActivity"); intent.putExtra("path", scriptFile.getAbsolutePath()); - intent.putExtra("url", url); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { context.startActivity(intent); @@ -85,6 +86,13 @@ public class AutoJsUtil { AutoJsUtil.flag = true; } synchronized (taskLock) { + try { + infoUpload(context, MainActivity.androidId, scriptResult); + } catch (IOException e) { + // 例如:可以显示给用户一条错误消息 + Log.e("AutoJsUtil", "File upload failed: " + e.getMessage()); + } + 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 SCRIPT_RESULT_KEY = "result"; - private static final Object lock = new Object(); - - + private static final String SCRIPT_RESULT_KEY = "package"; public static void stopAutojsScript(Context context) { // 停止运行脚本的 Intent @@ -157,5 +162,4 @@ public class AutoJsUtil { Log.e("AutoJsUtil", "目标活动未找到: org.autojs.autojs.external.open.StopServiceActivity"); } } - } diff --git a/app/src/main/java/com/example/studyapp/device/ChangeDeviceInfoUtil.java b/app/src/main/java/com/example/studyapp/device/ChangeDeviceInfoUtil.java index 4af97db..00615c6 100644 --- a/app/src/main/java/com/example/studyapp/device/ChangeDeviceInfoUtil.java +++ b/app/src/main/java/com/example/studyapp/device/ChangeDeviceInfoUtil.java @@ -2,24 +2,21 @@ package com.example.studyapp.device; import android.content.ContentResolver; import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; import android.net.Uri; -import android.text.TextUtils; 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.ShellUtils; -import com.google.android.gms.ads.identifier.AdvertisingIdClient; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -28,9 +25,9 @@ import java.lang.reflect.Method; 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) { 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(); - public static void initialize(String country, int tag) { + public static void initialize(String country, int tag, MainActivity mainActivity) { executorService.submit(() -> { try { - String bigoJson = HttpUtil.requestGet(buildBigoUrl(country, tag)); - String afJson = HttpUtil.requestGet(buildAfUrl(country, tag )); - - bigoDeviceObject = new JSONObject(bigoJson).optJSONObject("device"); - afDeviceObject = new JSONObject(afJson).optJSONObject("device"); - - if (bigoDeviceObject == null || afDeviceObject == null) { - throw new JSONException("Device object is missing in the response JSON"); + // 发起网络请求并捕获可能的异常 + String bigoJson; + String afJson; + try { + bigoJson = HttpUtil.requestGet(buildBigoUrl(country, tag)); + afJson = HttpUtil.requestGet(buildAfUrl(country, tag)); + } catch (IOException ioException) { + 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) { - 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) { Log.e("ChangeDeviceInfoUtil", "Required device JSON objects are not initialized"); @@ -95,14 +127,31 @@ public class ChangeDeviceInfoUtil { String resolution = bigoDeviceObject.optString("resolution"); String vendor = bigoDeviceObject.optString("vendor"); int batteryScale = bigoDeviceObject.optInt("bat_scale"); - //String model = deviceObject.optString("model"); + // 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"); - //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 model = afDeviceObject.optString(".model"); String brand = afDeviceObject.optString(".brand"); @@ -118,33 +167,93 @@ public class ChangeDeviceInfoUtil { String langCode = afDeviceObject.optString(".lang_code"); String cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi"); 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 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 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"; - Map params = new HashMap<>(); - params.put("aff", "12345"); - params.put("aff_sub6", "sub6Value"); - params.put("offer_id", "offer123"); - params.put("adx_bundle_id", "adxBundle123"); - params.put("affiliate_id", "affiliateID123"); - params.put("currency", "USD"); - params.put("payout", "5.0"); - params.put("transaction_id",""); - params.put("ip",HttpUtil.getLocalIpAddress()); - params.put("aff_sub4", "English"); - params.put("aff_sub3", "AdvertisingID123"); - - params.put("adx_id", advertiserId); - params.put("ua",userAgent); - params.put("model", model); - params.put("os_version", osVer); + 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", ""); // 自动处理分辨率信息 // int widthPixels = Integer.parseInt(resolution.split("x")[0]); // int heightPixels = Integer.parseInt(resolution.split("x")[1]); - + // // 更新屏幕显示相关参数 // JSONObject displayMetrics = new JSONObject(); // displayMetrics.put("widthPixels", widthPixels); @@ -152,7 +261,7 @@ public class ChangeDeviceInfoUtil { // displayMetrics.put("densityDpi", dpi); // 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 + ".sim_country", simCountry, 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 + ".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 + ".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); @@ -177,7 +286,7 @@ public class ChangeDeviceInfoUtil { // **tz** (时区) 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 + ".model", model, context); callVCloudSettings_put(current_pkg_name + ".brand", brand, context); @@ -203,74 +312,34 @@ public class ChangeDeviceInfoUtil { if (!ShellUtils.hasRootAccess()) { 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.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 ); + 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); + // Native.setBootId(bootId); // 修改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 - 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); + 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); - - // 填充占位符 - return HttpUtil.fillUrlPlaceholders(url, params); + 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) { Log.e("ChangeDeviceInfoUtil", "Error occurred while changing device info", e); throw new RuntimeException("Error occurred in changeDeviceInfo", e); diff --git a/app/src/main/java/com/example/studyapp/device/DeviceConfiguration.json b/app/src/main/java/com/example/studyapp/device/DeviceConfiguration.json new file mode 100644 index 0000000..4756d93 --- /dev/null +++ b/app/src/main/java/com/example/studyapp/device/DeviceConfiguration.json @@ -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" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/studyapp/utils/ClashUtil.java b/app/src/main/java/com/example/studyapp/proxy/ClashUtil.java similarity index 98% rename from app/src/main/java/com/example/studyapp/utils/ClashUtil.java rename to app/src/main/java/com/example/studyapp/proxy/ClashUtil.java index ebf94d8..0bc9528 100644 --- a/app/src/main/java/com/example/studyapp/utils/ClashUtil.java +++ b/app/src/main/java/com/example/studyapp/proxy/ClashUtil.java @@ -1,4 +1,4 @@ -package com.example.studyapp.utils; +package com.example.studyapp.proxy; import android.content.BroadcastReceiver; import android.content.Context; @@ -10,7 +10,6 @@ import java.io.IOException; import java.util.concurrent.CountDownLatch; import okhttp3.Call; import okhttp3.Callback; -import okhttp3.Credentials; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; diff --git a/app/src/main/java/com/example/studyapp/service/MyAccessibilityService.java b/app/src/main/java/com/example/studyapp/service/MyAccessibilityService.java index 40b975c..36b0edc 100644 --- a/app/src/main/java/com/example/studyapp/service/MyAccessibilityService.java +++ b/app/src/main/java/com/example/studyapp/service/MyAccessibilityService.java @@ -1,16 +1,22 @@ package com.example.studyapp.service; +import android.Manifest; import android.accessibilityservice.AccessibilityService; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.os.Build; import android.os.IBinder; import android.view.accessibility.AccessibilityEvent; +import android.widget.Toast; import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; import com.example.studyapp.MainActivity; import com.example.studyapp.R; @@ -35,28 +41,56 @@ public class MyAccessibilityService extends AccessibilityService { private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - String channelId = "2"; - String channelName = "Foreground Service"; - int importance = NotificationManager.IMPORTANCE_LOW; - NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); - NotificationManager notificationManager = getSystemService(NotificationManager.class); - if (notificationManager != null) { - notificationManager.createNotificationChannel(channel); + NotificationChannel channel = new NotificationChannel( + "2", + "无障碍服务", + NotificationManager.IMPORTANCE_LOW + ); + channel.setDescription("此服务在后台运行,提供无障碍支持。"); + NotificationManager manager = getSystemService(NotificationManager.class); + if (manager != null) { + manager.createNotificationChannel(channel); } } } private void startForegroundService() { createNotificationChannel(); + Intent notificationIntent = new Intent(this, MainActivity.class); int pendingIntentFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, pendingIntentFlags); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "2") .setContentTitle("无障碍服务运行中") .setContentText("此服务正在持续运行") .setSmallIcon(R.drawable.ic_launcher_foreground) .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); + } } } diff --git a/app/src/main/java/com/example/studyapp/task/AfInfo.java b/app/src/main/java/com/example/studyapp/task/AfInfo.java new file mode 100644 index 0000000..793da1b --- /dev/null +++ b/app/src/main/java/com/example/studyapp/task/AfInfo.java @@ -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; +} diff --git a/app/src/main/java/com/example/studyapp/task/BigoInfo.java b/app/src/main/java/com/example/studyapp/task/BigoInfo.java new file mode 100644 index 0000000..d67d5c6 --- /dev/null +++ b/app/src/main/java/com/example/studyapp/task/BigoInfo.java @@ -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; +} diff --git a/app/src/main/java/com/example/studyapp/task/DeviceInfo.java b/app/src/main/java/com/example/studyapp/task/DeviceInfo.java new file mode 100644 index 0000000..d69a9bc --- /dev/null +++ b/app/src/main/java/com/example/studyapp/task/DeviceInfo.java @@ -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; +} \ No newline at end of file diff --git a/app/src/main/java/com/example/studyapp/task/TaskUtil.java b/app/src/main/java/com/example/studyapp/task/TaskUtil.java new file mode 100644 index 0000000..0a1e1ee --- /dev/null +++ b/app/src/main/java/com/example/studyapp/task/TaskUtil.java @@ -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; +} + diff --git a/app/src/main/jniLibs/arm64-v8a/libnative.so b/app/src/main/jniLibs/arm64-v8a/libnative.so index 1935dd8..2366804 100644 Binary files a/app/src/main/jniLibs/arm64-v8a/libnative.so and b/app/src/main/jniLibs/arm64-v8a/libnative.so differ diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 718a98a..ce01498 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -11,5 +11,6 @@ 8.211.204.20 127.0.0.1 8.217.137.25 + 47.238.96.231 diff --git a/app/src/test/java/com/example/studyapp/task/TaskUtilTest.java b/app/src/test/java/com/example/studyapp/task/TaskUtilTest.java new file mode 100644 index 0000000..aaaed11 --- /dev/null +++ b/app/src/test/java/com/example/studyapp/task/TaskUtilTest.java @@ -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 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 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\"")); + } +} \ No newline at end of file