1 package edu.jiangxin.apktoolbox.android.dumpsys.alarm; 2 3 import edu.jiangxin.apktoolbox.file.core.EncoderDetector; 4 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel; 5 import edu.jiangxin.apktoolbox.swing.treetable.MyAbstractTreeTableModel; 6 import edu.jiangxin.apktoolbox.swing.treetable.MyTreeTable; 7 import edu.jiangxin.apktoolbox.utils.Constants; 8 import org.apache.commons.exec.CommandLine; 9 import org.apache.commons.exec.DefaultExecutor; 10 import org.apache.commons.exec.PumpStreamHandler; 11 import org.apache.commons.io.FileUtils; 12 import org.apache.commons.lang3.StringUtils; 13 14 import javax.swing.*; 15 import java.awt.*; 16 import java.awt.event.ActionEvent; 17 import java.awt.event.ActionListener; 18 import java.io.ByteArrayOutputStream; 19 import java.io.File; 20 import java.io.IOException; 21 import java.nio.charset.Charset; 22 import java.util.ArrayList; 23 import java.util.List; 24 25 public class DumpsysAlarmPanel extends EasyPanel { 26 27 private JPanel operationPanel; 28 29 private JPanel lastDateTimePanel; 30 31 private JTextField lastDateTimeField; 32 33 private JPanel systemUptime1Panel; 34 35 private JTextField systemUptime1Field; 36 37 private JPanel systemUptime2Panel; 38 39 private JTextField systemUptime2Field; 40 41 private JPanel packagePanel; 42 43 private JTextField packageField; 44 45 private JTabbedPane tabbedPane; 46 47 private JPanel tabularFormTabPanel; 48 49 private JPanel rawSourceTabPanel; 50 51 private JEditorPane editorPane; 52 53 private List<AlarmTreeTableDataNode> children; 54 55 private String alarmInfoString; 56 57 private boolean alarmInfoValid; 58 59 public DumpsysAlarmPanel() throws HeadlessException { 60 super(); 61 } 62 63 @Override 64 public void initUI() { 65 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS); 66 setLayout(boxLayout); 67 68 createOptionPanel(); 69 add(operationPanel); 70 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 71 72 createLastDateTimePanel(); 73 add(lastDateTimePanel); 74 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 75 76 createSystemUptime1Panel(); 77 add(systemUptime1Panel); 78 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 79 80 createSystemUptime2Panel(); 81 add(systemUptime2Panel); 82 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 83 84 createPackagePanel(); 85 add(packagePanel); 86 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 87 88 createTabbedPane(); 89 add(tabbedPane); 90 } 91 92 private void createOptionPanel() { 93 operationPanel = new JPanel(); 94 operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS)); 95 96 JButton loadFromDeviceButton = new JButton("Load From Device"); 97 loadFromDeviceButton.addActionListener(new LoadFromDeviceButtonActionListener()); 98 99 JButton loadFromFileButton = new JButton("Load From File"); 100 loadFromFileButton.addActionListener(new LoadFromFileButtonActionListener()); 101 102 operationPanel.add(loadFromDeviceButton); 103 operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 104 operationPanel.add(loadFromFileButton); 105 operationPanel.add(Box.createHorizontalGlue()); 106 } 107 108 private void createLastDateTimePanel() { 109 lastDateTimePanel = new JPanel(); 110 lastDateTimePanel.setLayout(new BoxLayout(lastDateTimePanel, BoxLayout.X_AXIS)); 111 112 JLabel lastDateTimeLabel = new JLabel("Last DateTime"); 113 114 lastDateTimeField = new JTextField(); 115 116 lastDateTimePanel.add(lastDateTimeLabel); 117 lastDateTimePanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 118 lastDateTimePanel.add(lastDateTimeField); 119 } 120 121 private void createSystemUptime1Panel() { 122 systemUptime1Panel = new JPanel(); 123 systemUptime1Panel.setLayout(new BoxLayout(systemUptime1Panel, BoxLayout.X_AXIS)); 124 125 JLabel systemUptime1Label = new JLabel("System Uptime(ms)"); 126 127 systemUptime1Field = new JTextField(); 128 129 systemUptime1Panel.add(systemUptime1Label); 130 systemUptime1Panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 131 systemUptime1Panel.add(systemUptime1Field); 132 } 133 134 private void createSystemUptime2Panel() { 135 systemUptime2Panel = new JPanel(); 136 systemUptime2Panel.setLayout(new BoxLayout(systemUptime2Panel, BoxLayout.X_AXIS)); 137 138 JLabel systemUptime2Label = new JLabel("System Uptime"); 139 140 systemUptime2Field = new JTextField(); 141 142 systemUptime2Panel.add(systemUptime2Label); 143 systemUptime2Panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 144 systemUptime2Panel.add(systemUptime2Field); 145 } 146 147 private void createPackagePanel() { 148 packagePanel = new JPanel(); 149 packagePanel.setLayout(new BoxLayout(packagePanel, BoxLayout.X_AXIS)); 150 151 JLabel packageLabel = new JLabel("Package"); 152 153 packageField = new JTextField(); 154 155 packagePanel.add(packageLabel); 156 packagePanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 157 packagePanel.add(packageField); 158 } 159 160 private void createTabbedPane() { 161 tabbedPane = new JTabbedPane(); 162 163 tabularFormTabPanel = new JPanel(); 164 createTable(); 165 tabbedPane.addTab("Tabular Form", null, tabularFormTabPanel, "Tabular Form"); 166 167 rawSourceTabPanel = new JPanel(); 168 createRawSource(); 169 tabbedPane.addTab("Raw source", null, rawSourceTabPanel, "Raw source"); 170 } 171 172 private void createTable() { 173 children = new ArrayList<>(); 174 AlarmTreeTableDataNode root = new AlarmTreeTableDataNode("Root", "", "", "", "", children); 175 MyAbstractTreeTableModel treeTableModel = new AlarmTreeTableDataModel(root); 176 MyTreeTable myTreeTable = new MyTreeTable(treeTableModel); 177 JScrollPane scrollPane = new JScrollPane(myTreeTable); 178 scrollPane.setPreferredSize(new Dimension(Constants.DEFAULT_SCROLL_PANEL_WIDTH, Constants.DEFAULT_SCROLL_PANEL_HEIGHT)); 179 tabularFormTabPanel.add(scrollPane); 180 } 181 182 private void createRawSource() { 183 editorPane = new JEditorPane("text/plain", ""); 184 editorPane.setEditable(false); 185 JScrollPane scrollPane = new JScrollPane(editorPane); 186 scrollPane.setPreferredSize(new Dimension(Constants.DEFAULT_SCROLL_PANEL_WIDTH, Constants.DEFAULT_SCROLL_PANEL_HEIGHT)); 187 rawSourceTabPanel.add(scrollPane); 188 } 189 190 private void getAlarmInfoStringFromDevice() { 191 final String cmd = "adb shell dumpsys alarm"; 192 logger.info(cmd); 193 try (ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 194 ByteArrayOutputStream errStream = new ByteArrayOutputStream() 195 ) { 196 CommandLine commandLine = CommandLine.parse(cmd); 197 DefaultExecutor exec = new DefaultExecutor(); 198 PumpStreamHandler streamHandler = new PumpStreamHandler(outStream, errStream); 199 exec.setStreamHandler(streamHandler); 200 int exitValue = exec.execute(commandLine); 201 logger.info("exitValue: [" + exitValue + "]"); 202 203 alarmInfoString = outStream.toString("UTF-8"); 204 alarmInfoValid = !StringUtils.isEmpty(alarmInfoString); 205 if (!alarmInfoValid) { 206 alarmInfoString = errStream.toString("UTF-8"); 207 } 208 } catch (IOException ioe) { 209 logger.error("exec fail", ioe.getMessage()); 210 } 211 } 212 213 private void getAlarmInfoStringFromFile() { 214 JFileChooser jfc = new JFileChooser(); 215 jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); 216 jfc.setDialogTitle("Select a dumpsys alarm file"); 217 int ret = jfc.showDialog(new JLabel(), null); 218 File file = jfc.getSelectedFile(); 219 switch (ret) { 220 case JFileChooser.APPROVE_OPTION: 221 try { 222 alarmInfoString = FileUtils.readFileToString(file, Charset.forName(EncoderDetector.judgeFile(file.getCanonicalPath()))); 223 alarmInfoValid = true; 224 } catch (IOException ex) { 225 logger.error("readFileToString failed", ex); 226 alarmInfoValid = false; 227 } 228 break; 229 default: 230 break; 231 } 232 } 233 234 private void updateUIFromString() { 235 alarmInfoString = StringUtils.isEmpty(alarmInfoString) ? "" : alarmInfoString; 236 editorPane.setText(alarmInfoString); 237 238 if (!alarmInfoValid) { 239 children.clear(); 240 return; 241 } 242 243 String tmpInputString = alarmInfoString; 244 245 String timeInformation = StringUtils.substringBetween(alarmInfoString, "nowRTC=", "mLastTimeChangeClockTime=").trim().replace(" ", "="); 246 String[] components = timeInformation.split("="); 247 248 SharedData sharedData = SharedData.getInstance(); 249 sharedData.setTimestamp(Long.valueOf(components[0])); 250 sharedData.setDateTime(components[1] + " " + components[2]); 251 sharedData.setUptimeMs(Long.valueOf(components[4])); 252 253 long uptimeSeconds = sharedData.getUptimeMs() / 1000; 254 long uptimeHours = uptimeSeconds / 3600; 255 long uptimeMinutes = (uptimeSeconds % 3600) / 60; 256 long uptimeSecs = uptimeSeconds % 60; 257 258 systemUptime1Field.setText(String.valueOf(sharedData.getUptimeMs())); 259 systemUptime2Field.setText(uptimeHours + ":" + uptimeMinutes + ":" + uptimeSecs); 260 lastDateTimeField.setText(sharedData.getDateTime()); 261 262 String pendingAlarmBatchesCountStr = StringUtils.substringBetween(tmpInputString, "Pending alarm batches: ", System.getProperty("line.separator")); 263 int pendingAlarmBatches = Integer.valueOf(pendingAlarmBatchesCountStr); 264 tmpInputString = StringUtils.substringAfter(tmpInputString, "Pending alarm batches: "); 265 266 List<AlarmBatch> listBatches = new ArrayList<>(); 267 while (true) { 268 String batchDefinition = StringUtils.substringBetween(tmpInputString, "Batch{", "}:" + System.getProperty("line.separator")); 269 if (StringUtils.isEmpty(batchDefinition)) { 270 break; 271 } 272 AlarmBatch alarmBatch = AlarmBatch.fromString(batchDefinition); 273 if (alarmBatch == null) { 274 break; 275 } 276 String alarmListDefinition = StringUtils.substringBetween(tmpInputString, "}:" + System.getProperty("line.separator"), "Batch{"); 277 alarmListDefinition.replace("ELAPSED_WAKEUP", "ELAPSED").replace("RTC_WAKEUP", "ELAPSED").replace("RTC", "ELAPSED"); 278 String[] alarmListTmp = alarmListDefinition.split("ELAPSED"); 279 List<String> alarmList = new ArrayList<>(); 280 for (int i = 0; i < alarmListTmp.length; i++) { 281 if (StringUtils.isNotEmpty(alarmListTmp[i]) && StringUtils.isNotBlank(alarmListTmp[i])) { 282 alarmList.add(alarmListTmp[i]); 283 } 284 } 285 286 for (int i = 0; i < alarmList.size(); ++i) { 287 Alarm alarm = Alarm.fromString(alarmList.get(i)); 288 if (StringUtils.isNotEmpty(packageField.getText()) && StringUtils.equals(alarm.getOwnerPackageName(), packageField.getText())) { 289 continue; 290 } 291 alarmBatch.appendAlarm(alarm); 292 } 293 294 listBatches.add(alarmBatch); 295 if (listBatches.size() == pendingAlarmBatches) { 296 break; 297 } 298 299 tmpInputString = StringUtils.substringAfter(tmpInputString, "Batch{"); 300 } 301 children.clear(); 302 children.addAll(alarmBatches2AlarmTreeTableDataNodeList(listBatches)); 303 } 304 305 private List<AlarmTreeTableDataNode> alarmBatches2AlarmTreeTableDataNodeList(List<AlarmBatch> listBatches) { 306 List<AlarmTreeTableDataNode> sonList = new ArrayList<>(); 307 for (AlarmBatch alarmBatch : listBatches) { 308 List<AlarmTreeTableDataNode> grandsonList = new ArrayList<>(); 309 List<Alarm> alarmList = alarmBatch.getListAlarms(); 310 for (Alarm alarm : alarmList) { 311 String objectId = alarm.getSignature() + " [" + alarm.getId() + "]"; 312 AlarmTreeTableDataNode grandson = new AlarmTreeTableDataNode(objectId, alarm.getOwnerPackageName(), alarm.getAlarmType(), String.valueOf(alarm.getWhen()),String.valueOf(alarm.getWhen()), null); 313 grandsonList.add(grandson); 314 } 315 String objectId = alarmBatch.getSignature() + " [" + alarmBatch.getId() + "] (" + alarmBatch.getAlarmCount() + " alarms)"; 316 AlarmTreeTableDataNode son = new AlarmTreeTableDataNode(objectId, null, null, null, null, grandsonList); 317 sonList.add(son); 318 } 319 return sonList; 320 } 321 322 private final class LoadFromDeviceButtonActionListener implements ActionListener { 323 @Override 324 public void actionPerformed(ActionEvent e) { 325 getAlarmInfoStringFromDevice(); 326 updateUIFromString(); 327 } 328 } 329 330 private final class LoadFromFileButtonActionListener implements ActionListener { 331 @Override 332 public void actionPerformed(ActionEvent e) { 333 getAlarmInfoStringFromFile(); 334 updateUIFromString(); 335 } 336 } 337 }