[feature] zip all logs (#999)
This commit is contained in:
parent
9d56cab7a4
commit
7e85205b8b
|
|
@ -37,7 +37,6 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.activity.MainActivity"
|
android:name=".ui.activity.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
@ -54,16 +53,6 @@
|
||||||
android:process=":error" />
|
android:process=":error" />
|
||||||
|
|
||||||
<receiver android:name=".receivers.ServiceReceiver" />
|
<receiver android:name=".receivers.ServiceReceiver" />
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.fileprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/file_paths" />
|
|
||||||
</provider>
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,10 @@ import org.lsposed.manager.receivers.LSPManagerServiceHolder;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import io.github.xposed.xposedservice.utils.ParceledListSlice;
|
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 {
|
try {
|
||||||
return verbose ? LSPManagerServiceHolder.getService().getVerboseLog() : LSPManagerServiceHolder.getService().getModulesLog();
|
return verbose ? LSPManagerServiceHolder.getService().getVerboseLog() : LSPManagerServiceHolder.getService().getModulesLog();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
|
@ -309,4 +311,13 @@ public class ConfigManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<String, ParcelFileDescriptor> getLogs() {
|
||||||
|
try {
|
||||||
|
return LSPManagerServiceHolder.getService().getLogs();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(App.TAG, Log.getStackTraceString(e));
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,15 @@
|
||||||
|
|
||||||
package org.lsposed.manager.ui.fragment;
|
package org.lsposed.manager.ui.fragment;
|
||||||
|
|
||||||
|
import static org.lsposed.manager.App.TAG;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -39,13 +40,11 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
import org.lsposed.manager.BuildConfig;
|
|
||||||
import org.lsposed.manager.ConfigManager;
|
import org.lsposed.manager.ConfigManager;
|
||||||
import org.lsposed.manager.R;
|
import org.lsposed.manager.R;
|
||||||
import org.lsposed.manager.databinding.FragmentLogsBinding;
|
import org.lsposed.manager.databinding.FragmentLogsBinding;
|
||||||
|
|
@ -53,10 +52,8 @@ import org.lsposed.manager.databinding.ItemLogBinding;
|
||||||
import org.lsposed.manager.util.LinearLayoutManagerFix;
|
import org.lsposed.manager.util.LinearLayoutManagerFix;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
|
@ -65,6 +62,9 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
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.core.os.FileUtils;
|
||||||
import rikka.recyclerview.RecyclerViewKt;
|
import rikka.recyclerview.RecyclerViewKt;
|
||||||
|
|
@ -80,21 +80,11 @@ public class LogsFragment extends BaseFragment {
|
||||||
new ActivityResultContracts.CreateDocument(),
|
new ActivityResultContracts.CreateDocument(),
|
||||||
uri -> {
|
uri -> {
|
||||||
if (uri == null) return;
|
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(() -> {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
var parcelFileDescriptor = ConfigManager.getLogs(verbose);
|
try (var os = new ZipOutputStream(requireContext().getContentResolver().openOutputStream(uri))) {
|
||||||
if (parcelFileDescriptor == null) {
|
os.setLevel(Deflater.BEST_COMPRESSION);
|
||||||
return;
|
zipLogs(os);
|
||||||
}
|
os.finish();
|
||||||
try (var os = requireContext().getContentResolver().openOutputStream(uri);
|
|
||||||
var is = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) {
|
|
||||||
FileUtils.copy(is, os);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
var str = getResources().getString(R.string.logs_save_failed);
|
var str = getResources().getString(R.string.logs_save_failed);
|
||||||
Snackbar.make(binding.snackbar, str + "\n" + e.getMessage(),
|
Snackbar.make(binding.snackbar, str + "\n" + e.getMessage(),
|
||||||
|
|
@ -163,13 +153,6 @@ public class LogsFragment extends BaseFragment {
|
||||||
} else if (itemId == R.id.menu_refresh) {
|
} else if (itemId == R.id.menu_refresh) {
|
||||||
reloadErrorLog();
|
reloadErrorLog();
|
||||||
return true;
|
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) {
|
} else if (itemId == R.id.menu_save) {
|
||||||
save();
|
save();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -188,7 +171,7 @@ public class LogsFragment extends BaseFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadErrorLog() {
|
private void reloadErrorLog() {
|
||||||
ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLogs(verbose);
|
ParcelFileDescriptor parcelFileDescriptor = ConfigManager.getLog(verbose);
|
||||||
if (parcelFileDescriptor != null) {
|
if (parcelFileDescriptor != null) {
|
||||||
new LogsReader().execute(parcelFileDescriptor.getFileDescriptor());
|
new LogsReader().execute(parcelFileDescriptor.getFileDescriptor());
|
||||||
} else {
|
} 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() {
|
private void save() {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
String filename = String.format(Locale.US,
|
String filename = String.format(Locale.ROOT, "LSPosed_%s.zip", now.toString());
|
||||||
"LSPosed_%s_%s.log",
|
|
||||||
verbose ? "Verbose" : "Modules",
|
|
||||||
now.toString());
|
|
||||||
saveLogsLauncher.launch(filename);
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
private class LogsReader extends AsyncTask<FileDescriptor, Integer, List<String>> {
|
private class LogsReader extends AsyncTask<FileDescriptor, Integer, List<String>> {
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,6 @@
|
||||||
android:title="@string/menuSaveToSd"
|
android:title="@string/menuSaveToSd"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_send"
|
|
||||||
android:icon="@drawable/ic_send"
|
|
||||||
android:title="@string/menuSend"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_refresh"
|
android:id="@+id/menu_refresh"
|
||||||
android:icon="@drawable/ic_refresh"
|
android:icon="@drawable/ic_refresh"
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ This file is part of LSPosed.
|
|
||||||
~
|
|
||||||
~ LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ LSPosed is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
~ Copyright (C) 2020 EdXposed Contributors
|
|
||||||
~ Copyright (C) 2021 LSPosed Contributors
|
|
||||||
-->
|
|
||||||
|
|
||||||
<paths>
|
|
||||||
<cache-path
|
|
||||||
name="log"
|
|
||||||
path="." />
|
|
||||||
</paths>
|
|
||||||
|
|
@ -2,6 +2,7 @@ package org.lsposed.lspd.service;
|
||||||
|
|
||||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||||
|
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.SELinux;
|
import android.os.SELinux;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
@ -22,6 +23,8 @@ import java.nio.file.attribute.PosixFilePermissions;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
class ConfigFileManager {
|
class ConfigFileManager {
|
||||||
static final File basePath = new File("/data/adb/lspd");
|
static final File basePath = new File("/data/adb/lspd");
|
||||||
|
|
@ -83,7 +86,7 @@ class ConfigFileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getNewLogFileName(String prefix) {
|
private static String getNewLogFileName(String prefix) {
|
||||||
return prefix + "-" + formatter.format(Instant.now()) + ".txt";
|
return prefix + "_" + formatter.format(Instant.now()) + ".txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
static File getNewVerboseLogPath() {
|
static File getNewVerboseLogPath() {
|
||||||
|
|
@ -94,6 +97,28 @@ class ConfigFileManager {
|
||||||
return new File(logDirPath, getNewLogFileName("modules"));
|
return new File(logDirPath, getNewLogFileName("modules"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Map<String, ParcelFileDescriptor> getLogs() {
|
||||||
|
var map = new LinkedHashMap<String, ParcelFileDescriptor>();
|
||||||
|
try {
|
||||||
|
putFds(map, logDirPath.toPath());
|
||||||
|
putFds(map, oldLogDirPath.toPath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "getLogs", e);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void putFds(Map<String, ParcelFileDescriptor> 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 {
|
private static String readText(File file) throws IOException {
|
||||||
return new String(Files.readAllBytes(file.toPath())).trim();
|
return new String(Files.readAllBytes(file.toPath())).trim();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import java.io.FileDescriptor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import io.github.xposed.xposedservice.utils.ParceledListSlice;
|
import io.github.xposed.xposedservice.utils.ParceledListSlice;
|
||||||
|
|
@ -281,4 +282,9 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
Log.w(TAG, "setHiddenIcon: ", e);
|
Log.w(TAG, "setHiddenIcon: ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ParcelFileDescriptor> getLogs() {
|
||||||
|
return ConfigFileManager.getLogs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,6 @@ interface ILSPManagerService {
|
||||||
boolean dex2oatFlagsLoaded() = 32;
|
boolean dex2oatFlagsLoaded() = 32;
|
||||||
|
|
||||||
void setHiddenIcon(boolean hide) = 33;
|
void setHiddenIcon(boolean hide) = 33;
|
||||||
|
|
||||||
|
Map<String,ParcelFileDescriptor> getLogs() = 34;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue