diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b19adc3a..baf5669a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -37,7 +37,6 @@ @@ -54,16 +53,6 @@ android:process=":error" /> - - - - diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index 84a0e59a..f8479767 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -35,8 +35,10 @@ import org.lsposed.manager.receivers.LSPManagerServiceHolder; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import io.github.xposed.xposedservice.utils.ParceledListSlice; @@ -173,7 +175,7 @@ public class ConfigManager { } } - public static ParcelFileDescriptor getLogs(boolean verbose) { + public static ParcelFileDescriptor getLog(boolean verbose) { try { return verbose ? LSPManagerServiceHolder.getService().getVerboseLog() : LSPManagerServiceHolder.getService().getModulesLog(); } catch (RemoteException e) { @@ -309,4 +311,13 @@ public class ConfigManager { return false; } } + + public static Map getLogs() { + try { + return LSPManagerServiceHolder.getService().getLogs(); + } catch (RemoteException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + return new HashMap<>(); + } + } } diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java index f36af5d3..f4531a53 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java @@ -20,14 +20,15 @@ package org.lsposed.manager.ui.fragment; +import static org.lsposed.manager.App.TAG; + import android.annotation.SuppressLint; -import android.content.Intent; -import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -39,13 +40,11 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.core.content.FileProvider; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayout; -import org.lsposed.manager.BuildConfig; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.R; import org.lsposed.manager.databinding.FragmentLogsBinding; @@ -53,10 +52,8 @@ import org.lsposed.manager.databinding.ItemLogBinding; import org.lsposed.manager.util.LinearLayoutManagerFix; import java.io.BufferedReader; -import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -65,6 +62,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import rikka.core.os.FileUtils; import rikka.recyclerview.RecyclerViewKt; @@ -80,21 +80,11 @@ public class LogsFragment extends BaseFragment { new ActivityResultContracts.CreateDocument(), uri -> { if (uri == null) return; - try { - // grantUriPermission might throw RemoteException on MIUI - requireContext().grantUriPermission(BuildConfig.APPLICATION_ID, uri, - Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - } catch (Exception e) { - e.printStackTrace(); - } AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - var parcelFileDescriptor = ConfigManager.getLogs(verbose); - if (parcelFileDescriptor == null) { - return; - } - try (var os = requireContext().getContentResolver().openOutputStream(uri); - var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) { - FileUtils.copy(is, os); + try (var os = new ZipOutputStream(requireContext().getContentResolver().openOutputStream(uri))) { + os.setLevel(Deflater.BEST_COMPRESSION); + zipLogs(os); + os.finish(); } catch (IOException e) { var str = getResources().getString(R.string.logs_save_failed); Snackbar.make(binding.snackbar, str + "\n" + e.getMessage(), @@ -163,13 +153,6 @@ public class LogsFragment extends BaseFragment { } else if (itemId == R.id.menu_refresh) { reloadErrorLog(); return true; - } else if (itemId == R.id.menu_send) { - try { - send(); - } catch (Exception e) { - e.printStackTrace(); - } - return true; } else if (itemId == R.id.menu_save) { save(); return true; @@ -188,7 +171,7 @@ public class LogsFragment extends BaseFragment { } private void reloadErrorLog() { - ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLogs(verbose); + ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLog(verbose); if (parcelFileDescriptor != null) { new LogsReader().execute(parcelFileDescriptor.getFileDescriptor()); } else { @@ -209,42 +192,43 @@ public class LogsFragment extends BaseFragment { } } - private void send() { - ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLogs(verbose); - if (parcelFileDescriptor == null) { - return; - } - LocalDateTime now = LocalDateTime.now(); - String filename = String.format(Locale.US, - "LSPosed_%s_%s.log", - verbose ? "Verbose" : "Modules", - now.toString()); - File cacheFile = new File(requireActivity().getCacheDir(), filename); - try (var os = new FileOutputStream(cacheFile); var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) { - FileUtils.copy(is, os); - } catch (IOException e) { - e.printStackTrace(); - return; - } - - Uri uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", cacheFile); - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_STREAM, uri); - sendIntent.setDataAndType(uri, "text/plain"); - sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - startActivity(Intent.createChooser(sendIntent, getResources().getString(R.string.menuSend))); - } - private void save() { LocalDateTime now = LocalDateTime.now(); - String filename = String.format(Locale.US, - "LSPosed_%s_%s.log", - verbose ? "Verbose" : "Modules", - now.toString()); + String filename = String.format(Locale.ROOT, "LSPosed_%s.zip", now.toString()); saveLogsLauncher.launch(filename); } + private static void zipLogs(ZipOutputStream os) { + var logs = ConfigManager.getLogs(); + logs.forEach((name, fd) -> { + try (var is = new FileInputStream(fd.getFileDescriptor())) { + os.putNextEntry(new ZipEntry(name)); + FileUtils.copy(is, os); + os.closeEntry(); + } catch (IOException e) { + Log.w(TAG, name, e); + } + }); + + try (var is = Runtime.getRuntime().exec("getprop").getInputStream()) { + os.putNextEntry(new ZipEntry("system_props.txt")); + FileUtils.copy(is, os); + os.closeEntry(); + } catch (IOException e) { + Log.w(TAG, "system_props.txt", e); + } + + var now = LocalDateTime.now(); + var name = "app_" + now.toString() + ".txt"; + try (var is = Runtime.getRuntime().exec("logcat -d").getInputStream()) { + os.putNextEntry(new ZipEntry(name)); + FileUtils.copy(is, os); + os.closeEntry(); + } catch (IOException e) { + Log.w(TAG, name, e); + } + } + @SuppressWarnings("deprecation") @SuppressLint("StaticFieldLeak") private class LogsReader extends AsyncTask> { diff --git a/app/src/main/res/menu/menu_logs.xml b/app/src/main/res/menu/menu_logs.xml index c8be2d36..1ff5c501 100644 --- a/app/src/main/res/menu/menu_logs.xml +++ b/app/src/main/res/menu/menu_logs.xml @@ -26,12 +26,6 @@ android:title="@string/menuSaveToSd" app:showAsAction="ifRoom" /> - - - - - - diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 22f1ecca..6f5dea12 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -2,6 +2,7 @@ package org.lsposed.lspd.service; import static org.lsposed.lspd.service.ServiceManager.TAG; +import android.os.ParcelFileDescriptor; import android.os.SELinux; import android.util.Log; @@ -22,6 +23,8 @@ import java.nio.file.attribute.PosixFilePermissions; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; class ConfigFileManager { static final File basePath = new File("/data/adb/lspd"); @@ -83,7 +86,7 @@ class ConfigFileManager { } private static String getNewLogFileName(String prefix) { - return prefix + "-" + formatter.format(Instant.now()) + ".txt"; + return prefix + "_" + formatter.format(Instant.now()) + ".txt"; } static File getNewVerboseLogPath() { @@ -94,6 +97,28 @@ class ConfigFileManager { return new File(logDirPath, getNewLogFileName("modules")); } + static Map getLogs() { + var map = new LinkedHashMap(); + try { + putFds(map, logDirPath.toPath()); + putFds(map, oldLogDirPath.toPath()); + } catch (IOException e) { + Log.e(TAG, "getLogs", e); + } + return map; + } + + private static void putFds(Map map, Path path) throws IOException { + Files.walkFileTree(path, new SimpleFileVisitor<>() { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + var name = path.relativize(file).toString(); + var fd = ParcelFileDescriptor.open(file.toFile(), ParcelFileDescriptor.MODE_READ_ONLY); + map.put(name, fd); + return FileVisitResult.CONTINUE; + } + }); + } + private static String readText(File file) throws IOException { return new String(Files.readAllBytes(file.toPath())).trim(); } diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 5c6a0065..5f2b28c4 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -45,6 +45,7 @@ import java.io.FileDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.List; +import java.util.Map; import de.robv.android.xposed.XposedBridge; import io.github.xposed.xposedservice.utils.ParceledListSlice; @@ -281,4 +282,9 @@ public class LSPManagerService extends ILSPManagerService.Stub { Log.w(TAG, "setHiddenIcon: ", e); } } + + @Override + public Map getLogs() { + return ConfigFileManager.getLogs(); + } } diff --git a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl index dd617d62..4624d85c 100644 --- a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl +++ b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl @@ -61,4 +61,6 @@ interface ILSPManagerService { boolean dex2oatFlagsLoaded() = 32; void setHiddenIcon(boolean hide) = 33; + + Map getLogs() = 34; }