[feature] zip all logs (#999)
This commit is contained in:
parent
9d56cab7a4
commit
7e85205b8b
|
|
@ -37,7 +37,6 @@
|
|||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
@ -54,16 +53,6 @@
|
|||
android:process=":error" />
|
||||
|
||||
<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>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -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<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;
|
||||
|
||||
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<FileDescriptor, Integer, List<String>> {
|
||||
|
|
|
|||
|
|
@ -26,12 +26,6 @@
|
|||
android:title="@string/menuSaveToSd"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_send"
|
||||
android:icon="@drawable/ic_send"
|
||||
android:title="@string/menuSend"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_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 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<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 {
|
||||
return new String(Files.readAllBytes(file.toPath())).trim();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, ParcelFileDescriptor> getLogs() {
|
||||
return ConfigFileManager.getLogs();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,4 +61,6 @@ interface ILSPManagerService {
|
|||
boolean dex2oatFlagsLoaded() = 32;
|
||||
|
||||
void setHiddenIcon(boolean hide) = 33;
|
||||
|
||||
Map<String,ParcelFileDescriptor> getLogs() = 34;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue