804 lines
29 KiB
Java
804 lines
29 KiB
Java
|
package com.example.retention.task;
|
|||
|
|
|||
|
import android.content.Context;
|
|||
|
import android.util.Log;
|
|||
|
import com.example.retention.utils.LogFileUtil;
|
|||
|
import com.example.retention.utils.ShellUtils;
|
|||
|
import com.google.gson.Gson;
|
|||
|
import com.google.gson.GsonBuilder;
|
|||
|
import com.google.gson.reflect.TypeToken;
|
|||
|
import java.io.BufferedOutputStream;
|
|||
|
import java.io.FileInputStream;
|
|||
|
import java.io.InputStream;
|
|||
|
import java.io.OutputStream;
|
|||
|
import java.lang.reflect.Type;
|
|||
|
import java.util.Map;
|
|||
|
import java.util.concurrent.TimeUnit;
|
|||
|
import java.util.zip.ZipEntry;
|
|||
|
import java.util.zip.ZipOutputStream;
|
|||
|
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 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.Builder()
|
|||
|
.connectTimeout(30, TimeUnit.SECONDS) // 连接超时
|
|||
|
.writeTimeout(60, TimeUnit.SECONDS) // 写入超时 (对上传很重要)
|
|||
|
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
|
|||
|
.build();
|
|||
|
|
|||
|
public static void postDeviceInfo(String androidId, String taskId,String packageName) {
|
|||
|
Log.i("TaskUtil", "postDeviceInfo called with androidId: " + androidId);
|
|||
|
|
|||
|
if (okHttpClient == null) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "HttpClient is not initialized", null);
|
|||
|
throw new IllegalStateException("HttpClient is not initialized");
|
|||
|
}
|
|||
|
|
|||
|
if (BASE_URL == null || BASE_URL.isEmpty()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "BASE_URL is not initialized", null);
|
|||
|
throw new IllegalStateException("BASE_URL is not initialized");
|
|||
|
}
|
|||
|
|
|||
|
Log.d("TaskUtil", "Creating payload for the request...");
|
|||
|
Payload payload = new Payload();
|
|||
|
payload.bigoDeviceObject = bigoDevice;
|
|||
|
payload.afDeviceObject = afDevice;
|
|||
|
payload.other = deviceInfo;
|
|||
|
|
|||
|
Gson gson = new GsonBuilder().serializeNulls().create();
|
|||
|
String jsonRequestBody = gson.toJson(payload);
|
|||
|
|
|||
|
Log.d("TaskUtil", "Request payload: " + jsonRequestBody);
|
|||
|
|
|||
|
if (packageName == null){
|
|||
|
packageName = "";
|
|||
|
}
|
|||
|
HttpUrl url = HttpUrl.parse(BASE_URL)
|
|||
|
.newBuilder()
|
|||
|
.addPathSegment("device_info_upload")
|
|||
|
.addQueryParameter("id", androidId)
|
|||
|
.addQueryParameter("taskId", taskId)
|
|||
|
.addQueryParameter("packageName", packageName)
|
|||
|
.build();
|
|||
|
|
|||
|
Log.d("TaskUtil", "Request URL: " + url.toString());
|
|||
|
|
|||
|
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonRequestBody);
|
|||
|
Log.d("TaskUtil", "Request body created");
|
|||
|
|
|||
|
Request request = new Request.Builder()
|
|||
|
.url(url)
|
|||
|
.post(body)
|
|||
|
.build();
|
|||
|
|
|||
|
Log.i("TaskUtil", "Starting network call...");
|
|||
|
try {
|
|||
|
Response response = okHttpClient.newCall(request).execute();
|
|||
|
try (ResponseBody responseBody = response.body()) {
|
|||
|
if (!response.isSuccessful()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Request failed with status: " + response.code(), null);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (responseBody != null) {
|
|||
|
String responseText = responseBody.string();
|
|||
|
Log.i("TaskUtil", "Request succeeded. Response: " + responseText);
|
|||
|
} else {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Response body is null", null);
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error while processing response: " + e.getMessage(), e);
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error during HTTP request: " + e.getMessage(), e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static String getDeviceInfoSync(String androidId,String taskId) {
|
|||
|
Log.d("TaskUtil", "getDeviceInfoSync called with androidId: " + androidId);
|
|||
|
|
|||
|
validate(); // 检查 BASE_URL 和 okHttpClient 的合法性
|
|||
|
|
|||
|
HttpUrl url = HttpUrl.parse(BASE_URL + "/device_info_download")
|
|||
|
.newBuilder()
|
|||
|
.addQueryParameter("androidId", androidId)
|
|||
|
.addQueryParameter("taskId",taskId)
|
|||
|
.build();
|
|||
|
|
|||
|
Log.d("TaskUtil", "Constructed URL for device info download: " + url.toString());
|
|||
|
|
|||
|
Request request = new Request.Builder()
|
|||
|
.url(url)
|
|||
|
.get()
|
|||
|
.build();
|
|||
|
|
|||
|
Log.d("TaskUtil", "Built HTTP request for device info download");
|
|||
|
|
|||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
|||
|
// 检查响应是否成功
|
|||
|
if (!response.isSuccessful()) {
|
|||
|
String errorMessage = "Unexpected response: Code=" + response.code() +
|
|||
|
", Message=" + response.message() +
|
|||
|
", URL=" + url.toString();
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", errorMessage, null);
|
|||
|
throw new IOException(errorMessage);
|
|||
|
}
|
|||
|
|
|||
|
ResponseBody responseBody = response.body();
|
|||
|
if (responseBody != null) {
|
|||
|
String responseString = responseBody.string();
|
|||
|
Log.i("TaskUtil", "Received response: " + responseString);
|
|||
|
return responseString;
|
|||
|
} else {
|
|||
|
String errorMessage = "Response body is null: URL=" + url.toString();
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", errorMessage, null);
|
|||
|
throw new IOException(errorMessage);
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
String errorMessage = "Error during HTTP request. URL=" + url.toString() +
|
|||
|
", Android ID=" + androidId;
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", errorMessage, e);
|
|||
|
return null; // 或考虑在上层抛出异常
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static String getApkPath(Context context, String packageName) {
|
|||
|
if (packageName == null || packageName.trim().isEmpty()) {
|
|||
|
throw new IllegalArgumentException("Package name must not be null or empty");
|
|||
|
}
|
|||
|
|
|||
|
String result = ShellUtils.getPackagePath(context, packageName);
|
|||
|
Log.d("TAG", "getApkPath: "+result);
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public static void infoUpload(Context context, String androidId, String packAge) throws IOException {
|
|||
|
|
|||
|
Log.i("TaskUtil", "infoUpload called with androidId: " + androidId + ", package: " + packAge);
|
|||
|
|
|||
|
if (packAge == null || packAge.isEmpty()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Package name is null or empty", null);
|
|||
|
throw new IllegalArgumentException("Package name cannot be null or empty");
|
|||
|
}
|
|||
|
|
|||
|
if (context == null) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Context is null", null);
|
|||
|
throw new IllegalArgumentException("Context cannot be null");
|
|||
|
}
|
|||
|
|
|||
|
if (androidId == null || androidId.isEmpty()) {
|
|||
|
Log.w("TaskUtil", "ANDROID_ID is null or empty");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
String apkSourceFile = getApkPath(context, packAge);
|
|||
|
Log.d("TaskUtil", "APK source directory: " + apkSourceFile);
|
|||
|
|
|||
|
// File externalDir = context.getExternalFilesDir(null);
|
|||
|
File externalDir = context.getCacheDir();
|
|||
|
if (externalDir == null) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "External storage directory is unavailable", null);
|
|||
|
throw new IOException("External storage directory is unavailable");
|
|||
|
}
|
|||
|
|
|||
|
String outputZipPath = new File(externalDir, androidId + "_" + packAge + ".zip").getAbsolutePath();
|
|||
|
Log.d("TaskUtil", "Output ZIP path: " + outputZipPath);
|
|||
|
|
|||
|
File zipFile = new File(outputZipPath);
|
|||
|
if (zipFile.exists()) {
|
|||
|
delFileSh(zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
File copiedAPKFile = new File(context.getCacheDir(), packAge+"_upload.apk");
|
|||
|
if (copiedAPKFile.exists()) {
|
|||
|
delFileSh(copiedAPKFile.getAbsolutePath());
|
|||
|
}
|
|||
|
copyFolderSh(apkSourceFile, copiedAPKFile.getAbsolutePath());
|
|||
|
// boolean success = clearUpFileInDst(copiedDir);
|
|||
|
// if (success) {
|
|||
|
// // 压缩APK文件
|
|||
|
// compressToZip(copiedDir, zipFile);
|
|||
|
// }
|
|||
|
|
|||
|
// 压缩APK文件
|
|||
|
compressToZip(copiedAPKFile, zipFile);
|
|||
|
// 上传压缩文件
|
|||
|
if (!zipFile.exists()) {
|
|||
|
Log.w("TaskUtil", "Upload file does not exist: " + outputZipPath);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
uploadFile(zipFile);
|
|||
|
|
|||
|
//uninstall
|
|||
|
String uninstall = "pm uninstall "+packAge;
|
|||
|
String chmodResult = ShellUtils.execRootCmdAndGetResult(uninstall);
|
|||
|
// 清理临时文件
|
|||
|
delFileSh(copiedAPKFile.getAbsolutePath());
|
|||
|
delFileSh(zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
public static boolean copyFolderSh(String oldPath, String newPath) {
|
|||
|
Log.i("TaskUtil", "start copyFolderSh : " + oldPath + " ; " + newPath);
|
|||
|
try {
|
|||
|
// 验证输入路径合法性
|
|||
|
if (oldPath == null || newPath == null || oldPath.isEmpty() || newPath.isEmpty()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Invalid path. oldPath: " + oldPath + ", newPath: " + newPath, null);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// 使用 File API 确保路径处理正确
|
|||
|
File src = new File(oldPath);
|
|||
|
File dst = new File(newPath);
|
|||
|
|
|||
|
if (!src.exists()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Source path does not exist: " + oldPath,null);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// 构造命令(注意 shell 特殊字符的转义)
|
|||
|
String safeOldPath = src.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
String safeNewPath = dst.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
String cmd = "cp -r -f \"" + safeOldPath + "\" \"" + safeNewPath + "\"";
|
|||
|
|
|||
|
Log.i("TaskUtil", "copyFolderSh cmd: " + cmd);
|
|||
|
|
|||
|
// 调用 MockTools 执行
|
|||
|
String result = ShellUtils.execRootCmdAndGetResult(cmd);
|
|||
|
String chmod = "chmod 777 \"" + safeNewPath + "\"";
|
|||
|
String chmodResult = ShellUtils.execRootCmdAndGetResult(chmod);
|
|||
|
// if (!TextUtils.isEmpty(chmodResult)) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "chmodResult. Result: " + chmodResult, null);
|
|||
|
// return false;
|
|||
|
// }
|
|||
|
if (result == null || result.trim().isEmpty()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Command execution failed. Result: " + result, null);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
Log.i("TaskUtil", "Command executed successfully: " + result);
|
|||
|
return true;
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error occurred during copyFolderSh operation", e);
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
*在给定的目标目录中清除特定的文件和目录。
|
|||
|
*子目录未命名为“缓存”,并且删除了大于3 MB的文件。
|
|||
|
*记录保留的文件和目录的路径。
|
|||
|
*
|
|||
|
* @param DST将处理其文件和子目录的目标目录
|
|||
|
* @return true如果目的目录存在并且已成功处理,则为false否则
|
|||
|
*/
|
|||
|
private static boolean clearUpFileInDst(File dst) {
|
|||
|
if (dst.exists()) {
|
|||
|
File[] files = dst.listFiles();
|
|||
|
if (files != null && files.length > 0) {
|
|||
|
for (File f : files) {
|
|||
|
if (f.isDirectory()) {
|
|||
|
if (!"cache".equalsIgnoreCase(f.getName())) {
|
|||
|
// f.delete();
|
|||
|
delFile(f);
|
|||
|
} else {
|
|||
|
Log.i("TaskUtil", "file need keep : " + f.getAbsolutePath());
|
|||
|
}
|
|||
|
} else {
|
|||
|
long fl = f.length();
|
|||
|
if (fl > 1024 * 1024 * 3) {
|
|||
|
// f.delete();
|
|||
|
delFile(f);
|
|||
|
} else {
|
|||
|
Log.i("TaskUtil", "file need keep : " + f.getAbsolutePath());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public static void delFileSh(String path) {
|
|||
|
Log.i("TaskUtil", "start delFileSh : " + path);
|
|||
|
|
|||
|
// 1. 参数校验
|
|||
|
if (path == null || path.isEmpty()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR,"TaskUtil", "Invalid or empty path provided.", null);
|
|||
|
return;
|
|||
|
}
|
|||
|
File file = new File(path);
|
|||
|
if (!file.exists()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR,"TaskUtil","File does not exist: " + path, null);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 3. 执行 Shell 命令
|
|||
|
try {
|
|||
|
String cmd = "rm -rf " + path;
|
|||
|
Log.i("TaskUtil", "Attempting to delete file using Shell command.");
|
|||
|
ShellUtils.execRootCmd(cmd);
|
|||
|
Log.i("TaskUtil", "File deletion successful for path: " + path);
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error occurred during delFileSh operation", e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static void delFile(File file) {
|
|||
|
if (file == null || !file.exists()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "File does not exist or is null.", null);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
String cmd = "rm -rf " + file.getAbsolutePath();
|
|||
|
Log.i("TaskUtil", "Deleting file: " + file.getName());
|
|||
|
ShellUtils.execRootCmd(cmd);
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error occurred during delFile operation", e);
|
|||
|
throw new RuntimeException("File deletion failed", e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static void compressToZip(File src, File dst) throws IOException {
|
|||
|
Log.d("TaskUtil", "Starting to compress single file...");
|
|||
|
|
|||
|
if (!src.exists() || !src.isFile()) {
|
|||
|
throw new IOException("Source file does not exist or is not a valid file: " + src.getAbsolutePath());
|
|||
|
}
|
|||
|
if (dst.exists()) {
|
|||
|
throw new IOException("Destination ZIP file already exists: " + dst.getAbsolutePath());
|
|||
|
}
|
|||
|
if (!dst.getParentFile().canWrite()) {
|
|||
|
throw new IOException("Parent directory of destination file is not writable: " + dst.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
try (FileOutputStream fos = new FileOutputStream(dst);
|
|||
|
ZipOutputStream zipOut = new ZipOutputStream(fos);
|
|||
|
FileInputStream fis = new FileInputStream(src)) {
|
|||
|
|
|||
|
String zipEntryName = src.getName();
|
|||
|
zipOut.putNextEntry(new ZipEntry(zipEntryName));
|
|||
|
|
|||
|
Log.d("TaskUtil", "Adding file to ZIP: " + zipEntryName);
|
|||
|
|
|||
|
byte[] buffer = new byte[4096];
|
|||
|
int bytesRead;
|
|||
|
while ((bytesRead = fis.read(buffer)) >= 0) {
|
|||
|
zipOut.write(buffer, 0, bytesRead);
|
|||
|
}
|
|||
|
|
|||
|
zipOut.closeEntry();
|
|||
|
Log.i("TaskUtil", "File successfully compressed to ZIP: " + dst.getName());
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error during file compression", e);
|
|||
|
throw e;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static void addDirToZip(File src, String parentPath, ZipOutputStream zipOut) throws IOException {
|
|||
|
File[] files = src.listFiles();
|
|||
|
if (files != null) {
|
|||
|
for (File file : files) {
|
|||
|
String zipEntryName = parentPath + file.getName();
|
|||
|
if (file.isDirectory()) {
|
|||
|
addDirToZip(file, zipEntryName + "/", zipOut);
|
|||
|
} else {
|
|||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
|||
|
zipOut.putNextEntry(new ZipEntry(zipEntryName));
|
|||
|
byte[] buffer = new byte[4096]; // 默认 Buffer 大小
|
|||
|
int bytesRead;
|
|||
|
while ((bytesRead = fis.read(buffer)) >= 0) {
|
|||
|
zipOut.write(buffer, 0, bytesRead);
|
|||
|
}
|
|||
|
zipOut.closeEntry();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void zipSh(File copyDir, File zipFile) {
|
|||
|
try {
|
|||
|
if (copyDir == null || zipFile == null || !copyDir.exists() || !zipFile.getParentFile().exists()) {
|
|||
|
throw new IllegalArgumentException("Invalid input directories or files.");
|
|||
|
}
|
|||
|
|
|||
|
// 获取父目录并确保路径合法
|
|||
|
String parentDir = copyDir.getParentFile().getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
String zipFilePath = zipFile.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
String copyDirName = copyDir.getName().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
|
|||
|
// 构造命令
|
|||
|
String cmd = "cd " + parentDir + " && tar -zcvf " + zipFilePath + " " + copyDirName;
|
|||
|
|
|||
|
Log.i("TaskUtil", "zipSh-> cmd:" + cmd.replace(parentDir, "[REDACTED]"));
|
|||
|
|
|||
|
String result = ShellUtils.execRootCmdAndGetResult(cmd);
|
|||
|
|
|||
|
if (result == null || result.contains("error")) {
|
|||
|
throw new IOException("Shell command execution failed: " + result);
|
|||
|
}
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Error in zipSh", e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void unZip(File destFile, File zipFile) {
|
|||
|
Log.d("TaskUtil", "unZip method called with parameters:");
|
|||
|
Log.d("TaskUtil", "Destination file: " + (destFile != null ? destFile.getAbsolutePath() : "null"));
|
|||
|
Log.d("TaskUtil", "ZIP file: " + (zipFile != null ? zipFile.getAbsolutePath() : "null"));
|
|||
|
|
|||
|
try {
|
|||
|
// 校验输入参数
|
|||
|
if (destFile == null || zipFile == null) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Destination file or ZIP file is null.", null);
|
|||
|
throw new IllegalArgumentException("Destination file or ZIP file cannot be null.");
|
|||
|
}
|
|||
|
|
|||
|
if (!zipFile.exists() || !zipFile.isFile()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Invalid ZIP file: " + zipFile.getAbsolutePath(), null);
|
|||
|
throw new IllegalArgumentException("Invalid ZIP file: " + zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
if (destFile.exists()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil", "Destination file already exists: " + destFile.getAbsolutePath(), null);
|
|||
|
throw new IllegalArgumentException("Destination file already exists and cannot be overwritten: " + destFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
File parentDir = destFile.getParentFile();
|
|||
|
if (!parentDir.exists()) {
|
|||
|
boolean created = parentDir.mkdirs();
|
|||
|
Log.d("TaskUtil", "Parent directory created: " + created + ", Path: " + parentDir.getAbsolutePath());
|
|||
|
if (!created) {
|
|||
|
throw new IOException("Failed to create parent directory: " + parentDir.getAbsolutePath());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 使用 Java 自带的 ZipInputStream 解压文件
|
|||
|
try (FileInputStream fis = new FileInputStream(zipFile);
|
|||
|
java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(fis);
|
|||
|
FileOutputStream fos = new FileOutputStream(destFile)) {
|
|||
|
|
|||
|
Log.d("TaskUtil", "ZipInputStream opened for ZIP file: " + zipFile.getAbsolutePath());
|
|||
|
|
|||
|
java.util.zip.ZipEntry zipEntry;
|
|||
|
int extractedFiles = 0;
|
|||
|
byte[] buffer = new byte[4096];
|
|||
|
int length;
|
|||
|
|
|||
|
while ((zipEntry = zis.getNextEntry()) != null) {
|
|||
|
if (extractedFiles > 0) {
|
|||
|
throw new IOException("ZIP file contains more than one entry, expected exactly one: " + zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
Log.d("TaskUtil", "Processing single entry: " + zipEntry.getName());
|
|||
|
|
|||
|
if (zipEntry.isDirectory()) {
|
|||
|
LogFileUtil.logAndWrite(android.util.Log.ERROR, "TaskUtil",
|
|||
|
"ZIP file entry is a directory but only a single file can be extracted: " + zipEntry.getName(), null);
|
|||
|
throw new IOException("ZIP file entry is a directory: " + zipEntry.getName());
|
|||
|
}
|
|||
|
|
|||
|
while ((length = zis.read(buffer)) > 0) {
|
|||
|
fos.write(buffer, 0, length);
|
|||
|
}
|
|||
|
extractedFiles++;
|
|||
|
zis.closeEntry();
|
|||
|
}
|
|||
|
|
|||
|
if (extractedFiles == 0) {
|
|||
|
throw new IOException("ZIP file does not contain entries to extract: " + zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
Log.i("TaskUtil", "Unzip successful. Extracted file to: " + destFile.getAbsolutePath());
|
|||
|
}
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR, "TaskUtil", "Error in unZip method", e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void unzipSh(File destinationDir, File zipFile) {
|
|||
|
try {
|
|||
|
if (destinationDir == null || zipFile == null) {
|
|||
|
throw new IllegalArgumentException("Destination directory or ZIP file cannot be null.");
|
|||
|
}
|
|||
|
|
|||
|
if (!zipFile.exists() || !zipFile.isFile()) {
|
|||
|
throw new IllegalArgumentException("Invalid ZIP file: " + zipFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
if (!destinationDir.exists()) {
|
|||
|
boolean mkdirs = destinationDir.mkdirs();
|
|||
|
if (!mkdirs) {
|
|||
|
throw new IOException("Failed to create destination directory: " + destinationDir.getAbsolutePath());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
String destPath = destinationDir.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
String zipFilePath = zipFile.getAbsolutePath().replace(" ", "\\ ").replace("\"", "\\\"");
|
|||
|
|
|||
|
// 构造解压命令
|
|||
|
String cmd = "unzip -o \"" + zipFilePath + "\" -d \"" + destPath + "\"";
|
|||
|
Log.i("TaskUtil", "unzipSh-> cmd: " + cmd.replace(zipFilePath, "[ZIP_FILE_PATH]").replace(destPath, "[DESTINATION_PATH]"));
|
|||
|
|
|||
|
// 执行命令
|
|||
|
String result = ShellUtils.execRootCmdAndGetResult(cmd);
|
|||
|
|
|||
|
// 检查返回结果
|
|||
|
if (result == null || result.contains("error")) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Shell command execution failed: " + result,null);
|
|||
|
throw new IOException("Shell command failed. Result: " + result);
|
|||
|
}
|
|||
|
|
|||
|
Log.i("TaskUtil", "Unzip successful. Extracted to: " + destinationDir.getAbsolutePath());
|
|||
|
} catch (Exception e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Error in unzipSh", e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void uploadFile(File fileToUpload) throws IOException {
|
|||
|
Log.d("TaskUtil", "Preparing to upload file...");
|
|||
|
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 + "/upload_package")
|
|||
|
.post(requestBody)
|
|||
|
.build();
|
|||
|
|
|||
|
Log.i("TaskUtil", "Starting file upload to: " + BASE_URL + "/upload_package");
|
|||
|
|
|||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
|||
|
ResponseBody body = response.body();
|
|||
|
if (response.isSuccessful() && body != null) {
|
|||
|
String responseBody = body.string();
|
|||
|
Log.i("TaskUtil", "File upload successful. Response: " + responseBody);
|
|||
|
} else {
|
|||
|
Log.w("TaskUtil", "File upload failed. Response: " + (response.message() != null ? response.message() : "Unknown"));
|
|||
|
if (body != null) {
|
|||
|
body.close();
|
|||
|
}
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "File upload failed", e);
|
|||
|
throw e;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static File downloadCodeFile(String fileName, File filesLocationDir) {
|
|||
|
String baseUrl = BASE_URL + "/download_code_file";
|
|||
|
String fullUrl = baseUrl + "?file_name=" + fileName;
|
|||
|
|
|||
|
Log.d("TaskUtil", "Download URL constructed: " + fullUrl); // 添加日志记录URL
|
|||
|
|
|||
|
Request request = new Request.Builder()
|
|||
|
.url(fullUrl)
|
|||
|
.get()
|
|||
|
.build();
|
|||
|
|
|||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
|||
|
Log.d("TaskUtil", "HTTP request executed. Response code: " + response.code()); // 记录响应代码
|
|||
|
|
|||
|
if (response.isSuccessful() && response.body() != null) {
|
|||
|
Log.d("TaskUtil", "Response is successful. Preparing to save file."); // 记录成功响应
|
|||
|
|
|||
|
return saveDownloadedFile(response, filesLocationDir, fileName);
|
|||
|
} else {
|
|||
|
Log.w("TaskUtil", "Download failed. HTTP code: " + response.code() + ", Message: " + response.message());
|
|||
|
|
|||
|
// 如果响应体不为空则记录内容
|
|||
|
if (response.body() != null) {
|
|||
|
try {
|
|||
|
String responseBody = response.body().string();
|
|||
|
LogFileUtil.logAndWrite(Log.INFO,"TaskUtil", "Response body: " + responseBody,null);
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Failed to read response body for logging", e);
|
|||
|
}
|
|||
|
} else {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Response body is null", null);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Error during file download", e);
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static File saveDownloadedFile(Response response, File filesLocationDir, String fileName) throws IOException {
|
|||
|
// 1. 更严格的目录检查和处理
|
|||
|
if (!filesLocationDir.exists()) {
|
|||
|
if (!filesLocationDir.mkdirs()) {
|
|||
|
throw new IOException("Failed to create directory: " + filesLocationDir.getAbsolutePath());
|
|||
|
}
|
|||
|
Log.d("TaskUtil", "Directory created: " + filesLocationDir.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
// 2. 检查目录是否可写
|
|||
|
if (!filesLocationDir.canWrite()) {
|
|||
|
throw new IOException("Directory not writable: " + filesLocationDir.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
// 3. 处理文件名冲突
|
|||
|
File saveFile = new File(filesLocationDir, fileName);
|
|||
|
if (saveFile.exists()) {
|
|||
|
saveFile.delete();
|
|||
|
}
|
|||
|
|
|||
|
// 4. 更完善的写入流程
|
|||
|
File tempFile = new File(filesLocationDir, fileName + ".tmp");
|
|||
|
try (InputStream is = response.body().byteStream();
|
|||
|
OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile))) {
|
|||
|
|
|||
|
byte[] buffer = new byte[8192];
|
|||
|
int bytesRead;
|
|||
|
while ((bytesRead = is.read(buffer)) != -1) {
|
|||
|
os.write(buffer, 0, bytesRead);
|
|||
|
}
|
|||
|
|
|||
|
// 5. 原子性重命名(确保文件完整)
|
|||
|
if (!tempFile.renameTo(saveFile)) {
|
|||
|
throw new IOException("Failed to rename temp file to: " + saveFile.getAbsolutePath());
|
|||
|
}
|
|||
|
|
|||
|
Log.i("TaskUtil", "File saved successfully to: " + saveFile.getAbsolutePath());
|
|||
|
return saveFile;
|
|||
|
|
|||
|
} catch (IOException e) {
|
|||
|
// 6. 清理不完整文件
|
|||
|
if (tempFile.exists()) {
|
|||
|
boolean deleted = tempFile.delete();
|
|||
|
Log.w("TaskUtil", "Deleted incomplete file: " + deleted);
|
|||
|
}
|
|||
|
throw e;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static void validate() {
|
|||
|
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");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static Map<String, String> getPackageInfo(String androidId) {
|
|||
|
Log.i("TaskUtil", "getPackageInfo called with androidId: " + androidId);
|
|||
|
|
|||
|
try {
|
|||
|
// 构造完整的URL
|
|||
|
HttpUrl url = HttpUrl.parse(BASE_URL + "/get_package_info")
|
|||
|
.newBuilder()
|
|||
|
.addQueryParameter("androidId", androidId)
|
|||
|
.build();
|
|||
|
|
|||
|
Log.d("TaskUtil", "Constructed URL: " + url.toString());
|
|||
|
|
|||
|
// 创建 HTTP 请求
|
|||
|
Request request = new Request.Builder()
|
|||
|
.url(url)
|
|||
|
.get()
|
|||
|
.build();
|
|||
|
|
|||
|
// 同步执行网络请求
|
|||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
|||
|
// 检查响应是否成功
|
|||
|
if (!response.isSuccessful()) {
|
|||
|
String errorMessage = "Unexpected response: Code=" + response.code() +
|
|||
|
", Message=" + response.message() +
|
|||
|
", URL=" + url.toString();
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", errorMessage, null);
|
|||
|
throw new IOException(errorMessage);
|
|||
|
}
|
|||
|
|
|||
|
// 解析响应内容
|
|||
|
ResponseBody responseBody = response.body();
|
|||
|
if (responseBody != null) {
|
|||
|
String responseString = responseBody.string();
|
|||
|
// 定义Gson对象
|
|||
|
Gson gson = new Gson();
|
|||
|
|
|||
|
// 指定解析的泛型类型为 Map<String, List<String>>
|
|||
|
Type type = new TypeToken<Map<String, String>>() {
|
|||
|
}.getType();
|
|||
|
|
|||
|
// 将JSON字符串转换为 Map
|
|||
|
Map<String, String> resultMap = gson.fromJson(responseString, type);
|
|||
|
|
|||
|
// 根据androidId获取对应的文件列表
|
|||
|
if (resultMap != null) {
|
|||
|
return resultMap;
|
|||
|
} else {
|
|||
|
Log.w("TaskUtil", "Android ID not found in response.");
|
|||
|
return null;
|
|||
|
}
|
|||
|
} else {
|
|||
|
String errorMessage = "Response body is null for URL=" + url.toString();
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", errorMessage, null);
|
|||
|
throw new IOException(errorMessage);
|
|||
|
}
|
|||
|
}
|
|||
|
} catch (IOException e) {
|
|||
|
LogFileUtil.logAndWrite(Log.ERROR,"TaskUtil", "Error during getPackageInfo request", e);
|
|||
|
}
|
|||
|
|
|||
|
return null; // 如果出错,返回null
|
|||
|
}
|
|||
|
|
|||
|
public static String execQueryTask(String androidId,String taskId) {
|
|||
|
return getDeviceInfoSync(androidId,taskId);
|
|||
|
}
|
|||
|
|
|||
|
public static void execSaveTask(Context context, String androidId, String taskId,String packName) {
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
postDeviceInfo(androidId, taskId,packName);
|
|||
|
} 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;
|
|||
|
}
|
|||
|
|