[feature] zip all logs (#999)

This commit is contained in:
vvb2060 2021-08-25 00:28:06 +08:00 committed by GitHub
parent 9d56cab7a4
commit 7e85205b8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 89 additions and 103 deletions

View File

@ -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>

View File

@ -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<>();
}
}
}

View File

@ -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>> {

View File

@ -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"

View File

@ -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>

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -61,4 +61,6 @@ interface ILSPManagerService {
boolean dex2oatFlagsLoaded() = 32;
void setHiddenIcon(boolean hide) = 33;
Map<String,ParcelFileDescriptor> getLogs() = 34;
}