DumpsysPanel.java
package edu.jiangxin.apktoolbox.android.dumpsys;
import edu.jiangxin.apktoolbox.android.dumpsys.tojson.IDumpsys2Json;
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
import edu.jiangxin.apktoolbox.utils.Constants;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.RTextScrollPane;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serial;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DumpsysPanel extends EasyPanel {
@Serial
private static final long serialVersionUID = 1L;
private JPanel dumpsysPanel;
private FilePanel dumpsysTargetDirPanel;
private JPanel dumpsysOptionPanel;
private JPanel dumpsysTypeChoosePanel;
private JPanel dumpsysOperationPanel;
private JButton dumpsysStartButton;
private JPanel analysisPanel;
private FilePanel analysisFilePanel;
private RSyntaxTextArea analysisOutputTextArea;
private RTextScrollPane analysisOutputScrollPane;
private JPanel analysisOperationPanel;
public DumpsysPanel() {
super();
}
@Override
public void initUI() {
setPreferredSize(new Dimension(700, 400));
JTabbedPane tabbedPane = new JTabbedPane();
add(tabbedPane);
createDumpsysPanel();
tabbedPane.addTab("Dumpsys", null, dumpsysPanel, "Dumpsys");
createAnalysisPanel();
tabbedPane.addTab("Analysis", null, analysisPanel, "Analysis");
}
@Override
public void afterPainted() {
super.afterPainted();
dumpsysTargetDirPanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysTargetDirPanel");
analysisFilePanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysAnalysisFilePanel");
}
private void createDumpsysPanel() {
dumpsysPanel = new JPanel();
BoxLayout layout = new BoxLayout(dumpsysPanel, BoxLayout.Y_AXIS);
dumpsysPanel.setLayout(layout);
dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createDumpsysTargetDirPanel();
dumpsysPanel.add(dumpsysTargetDirPanel);
dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createDumpsysOptionPanel();
dumpsysPanel.add(dumpsysOptionPanel);
dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createDumpsysOperationPanel();
dumpsysPanel.add(dumpsysOperationPanel);
}
private void createAnalysisPanel() {
analysisPanel = new JPanel();
BoxLayout layout = new BoxLayout(analysisPanel, BoxLayout.Y_AXIS);
analysisPanel.setLayout(layout);
dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createAnalysisTargetFilePanel();
analysisPanel.add(analysisFilePanel);
analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createAnalysisOutputPanel();
analysisPanel.add(analysisOutputScrollPane);
analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createAnalysisOperationPanel();
analysisPanel.add(analysisOperationPanel);
}
private void createAnalysisTargetFilePanel() {
analysisFilePanel = new FilePanel("Analysis File");
analysisFilePanel.initialize();
analysisFilePanel.setFileSelectionMode(JFileChooser.FILES_ONLY);
}
private void createAnalysisOutputPanel() {
analysisOutputTextArea = new RSyntaxTextArea();
analysisOutputTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON);
analysisOutputTextArea.setCodeFoldingEnabled(true);
analysisOutputTextArea.setEditable(false);
analysisOutputScrollPane = new RTextScrollPane(analysisOutputTextArea);
analysisOutputScrollPane.setPreferredSize(new Dimension(400, 250));
}
private void createAnalysisOperationPanel() {
analysisOperationPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(analysisOperationPanel, BoxLayout.X_AXIS);
analysisOperationPanel.setLayout(boxLayout);
JButton analysisStartButton = new JButton("Start");
analysisStartButton.setEnabled(true);
analysisStartButton.addActionListener(new AnalysisStartButtonActionListener());
analysisOperationPanel.add(analysisStartButton);
}
private void createDumpsysTargetDirPanel() {
dumpsysTargetDirPanel = new FilePanel("Target Directory");
dumpsysTargetDirPanel.initialize();
dumpsysTargetDirPanel.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
private void createDumpsysOptionPanel() {
dumpsysOptionPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(dumpsysOptionPanel, BoxLayout.Y_AXIS);
dumpsysOptionPanel.setLayout(boxLayout);
JPanel dumpsysCheckboxPanel = new JPanel();
dumpsysCheckboxPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
JCheckBox allSelectedCheckBox = new JCheckBox("Select All");
allSelectedCheckBox.setSelected(false);
allSelectedCheckBox.addActionListener(e -> {
boolean selected = allSelectedCheckBox.isSelected();
if (selected) {
Component[] components = dumpsysTypeChoosePanel.getComponents();
for (Component component : components) {
if (component instanceof JCheckBox checkBox) {
checkBox.setSelected(true);
checkBox.setEnabled(false);
}
}
} else {
Component[] components = dumpsysTypeChoosePanel.getComponents();
for (Component component : components) {
if (component instanceof JCheckBox checkBox) {
checkBox.setSelected(false);
checkBox.setEnabled(true);
}
}
}
});
dumpsysCheckboxPanel.add(allSelectedCheckBox);
dumpsysTypeChoosePanel = new JPanel();
dumpsysTypeChoosePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
dumpsysTypeChoosePanel.setPreferredSize(new Dimension(500, 200));
JCheckBox inputCheckBox = new JCheckBox("input");
inputCheckBox.setSelected(true);
JCheckBox windowCheckBox = new JCheckBox("window");
dumpsysTypeChoosePanel.add(windowCheckBox);
JCheckBox SurfaceFlingerCheckBox = new JCheckBox("SurfaceFlinger");
dumpsysTypeChoosePanel.add(SurfaceFlingerCheckBox);
JCheckBox activityCheckBox = new JCheckBox("activity");
dumpsysTypeChoosePanel.add(activityCheckBox);
JCheckBox accessibilityCheckBox = new JCheckBox("accessibility");
dumpsysTypeChoosePanel.add(accessibilityCheckBox);
JCheckBox packageCheckBox = new JCheckBox("package");
dumpsysTypeChoosePanel.add(packageCheckBox);
JCheckBox alarmCheckBox = new JCheckBox("alarm");
dumpsysTypeChoosePanel.add(alarmCheckBox);
JCheckBox inputMethodCheckBox = new JCheckBox("input_method");
dumpsysTypeChoosePanel.add(inputMethodCheckBox);
JCheckBox sensorServiceCheckBox = new JCheckBox("sensorservice");
dumpsysTypeChoosePanel.add(sensorServiceCheckBox);
JCheckBox accountCheckBox = new JCheckBox("account");
dumpsysTypeChoosePanel.add(accountCheckBox);
JCheckBox displayCheckBox = new JCheckBox("display");
dumpsysTypeChoosePanel.add(displayCheckBox);
JCheckBox powerCheckBox = new JCheckBox("power");
dumpsysTypeChoosePanel.add(powerCheckBox);
JCheckBox batteryCheckBox = new JCheckBox("battery");
dumpsysTypeChoosePanel.add(batteryCheckBox);
dumpsysOptionPanel.add(dumpsysCheckboxPanel);
dumpsysOptionPanel.add(new JSeparator(JSeparator.HORIZONTAL));
dumpsysOptionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
dumpsysOptionPanel.add(dumpsysTypeChoosePanel);
}
private void createDumpsysOperationPanel() {
dumpsysOperationPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(dumpsysOperationPanel, BoxLayout.X_AXIS);
dumpsysOperationPanel.setLayout(boxLayout);
dumpsysStartButton = new JButton("Start");
dumpsysStartButton.setEnabled(true);
dumpsysStartButton.addActionListener(new DumpsysStartButtonActionListener());
dumpsysOperationPanel.add(dumpsysStartButton);
}
class DumpsysStartButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
dumpsysStartButton.setEnabled(false);
File targetDirFile = dumpsysTargetDirPanel.getFile();
if (!targetDirFile.exists()) {
JOptionPane.showMessageDialog(getFrame(), "Target directory does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
return;
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(System.currentTimeMillis()));
File targetDirWithTimestamp = new File(targetDirFile, timeStamp);
if (!targetDirWithTimestamp.exists()) {
boolean success = targetDirWithTimestamp.mkdirs();
if (!success) {
JOptionPane.showMessageDialog(getFrame(), "Create target directory failed!", "ERROR", JOptionPane.ERROR_MESSAGE);
return;
}
}
Component[] components = dumpsysTypeChoosePanel.getComponents();
for (Component component : components) {
if (component instanceof JCheckBox checkBox) {
String dumpsysType = checkBox.getText();
if (checkBox.isSelected()) {
dumpsysAndSaveToFile(dumpsysType, targetDirWithTimestamp);
}
}
}
dumpsysStartButton.setEnabled(true);
}
private void dumpsysAndSaveToFile(String dumpsysType, File targetDir) {
String command = "adb shell dumpsys " + dumpsysType;
CommandLine cmdLine = CommandLine.parse(command);
DefaultExecutor executor = DefaultExecutor.builder().get();
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream();) {
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream);
executor.setStreamHandler(streamHandler);
int exitValue = executor.execute(cmdLine);
if (exitValue == 0) {
String result = outputStream.toString().trim();
if (StringUtils.isNotEmpty(result)) {
File outputFile = new File(targetDir, dumpsysType + ".dumpsys");
FileUtils.writeStringToFile(outputFile, result, "UTF-8");
}
} else {
String errorResult = errorStream.toString().trim();
if (StringUtils.isNotEmpty(errorResult)) {
File errorFile = new File(targetDir, dumpsysType + ".error");
FileUtils.writeStringToFile(errorFile, errorResult, "UTF-8");
}
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(getFrame(), "Execute command failed:\n" + ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
}
}
}
class AnalysisStartButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
File analysisFile = analysisFilePanel.getFile();
if (!analysisFile.exists()) {
JOptionPane.showMessageDialog(getFrame(), "Analysis file does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE);
return;
}
String fileNameWithoutExtension = FilenameUtils.getBaseName(analysisFile.getName());
String className = StringUtils.capitalize(fileNameWithoutExtension) + "2Json";
IDumpsys2Json dumpsys2Json = getDumpsys2JsonInstance(className);
if (dumpsys2Json != null) {
String jsonResult = dumpsys2Json.dumpsys2Json(analysisFile);
analysisOutputTextArea.setText(jsonResult);
analysisOutputTextArea.setCaretPosition(0);
}
}
private IDumpsys2Json getDumpsys2JsonInstance(String className) {
try {
String fullClassName = "edu.jiangxin.apktoolbox.android.dumpsys.tojson." + className;
Class<?> clazz = Class.forName(fullClassName);
if (IDumpsys2Json.class.isAssignableFrom(clazz)) {
return (IDumpsys2Json) clazz.getDeclaredConstructor().newInstance();
} else {
JOptionPane.showMessageDialog(getFrame(), "Class " + fullClassName + " does not implement IDumpsys2Json!", "ERROR", JOptionPane.ERROR_MESSAGE);
return null;
}
} catch (ClassNotFoundException e) {
JOptionPane.showMessageDialog(getFrame(), "Class not found: " + className, "ERROR", JOptionPane.ERROR_MESSAGE);
return null;
} catch (Exception e) {
JOptionPane.showMessageDialog(getFrame(), "Error loading class: " + e.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
return null;
}
}
}
}