1 package edu.jiangxin.apktoolbox.android.dumpsys.tojson;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.util.regex.Matcher;
7 import java.util.regex.Pattern;
8
9 import org.apache.commons.lang3.StringUtils;
10 import org.json.JSONArray;
11 import org.json.JSONObject;
12
13 public class Alarm2Json implements IDumpsys2Json {
14 @Override
15 public String dumpsys2Json(File file) {
16 String content;
17 try {
18 content = new String(Files.readAllBytes(file.toPath()));
19 } catch (IOException e) {
20 throw new RuntimeException("读取文件失败: " + file.getAbsolutePath(), e);
21 }
22 JSONObject result = new JSONObject();
23 parseSections(content, result);
24 return result.toString(2);
25 }
26
27 private void parseSections(String content, JSONObject result) {
28
29 String[] sections = content.split("\n\\s*\n");
30 for (String section : sections) {
31 String title = getSectionTitle(section);
32 if (title != null) {
33 Object value = parseSectionContent(title, section);
34 result.put(title, value);
35 } else if (section.trim().startsWith("nowRTC=")) {
36 Object value = parseSectionContent("TimeInfo", section);
37 result.put("TimeInfo", value);
38 }
39 }
40 }
41
42 private String getSectionTitle(String section) {
43 Matcher m = Pattern.compile("^\\s*([\\w .-]+):", Pattern.MULTILINE).matcher(section);
44 if (m.find()) {
45 return m.group(1).trim();
46 }
47 return null;
48 }
49
50 private Object parseSectionContent(String title, String section) {
51 if (title.equals("Settings") || title.equals("Feature Flags")) {
52 return parseKeyValueSection(section);
53 } else if (title.equals("Current AppStateTracker State")) {
54 return parseAppStateSection(section);
55 } else if (title.contains("pending alarms")) {
56 return parsePendingAlarms(section);
57 } else if (title.contains("TimeInfo")) {
58 return parseTimeInfoSection(section);
59 } else {
60 return section.trim();
61 }
62 }
63
64 private JSONObject parseKeyValueSection(String section) {
65 JSONObject obj = new JSONObject();
66 Matcher m = Pattern.compile("^\\s*([\\w._-]+)=(.+)$", Pattern.MULTILINE).matcher(section);
67 while (m.find()) {
68 obj.put(m.group(1).trim(), m.group(2).trim());
69 }
70 return obj;
71 }
72
73 private JSONObject parseAppStateSection(String section) {
74 JSONObject obj = new JSONObject();
75 Matcher m = Pattern.compile("^\\s*([\\w .-]+): (.+)$", Pattern.MULTILINE).matcher(section);
76 while (m.find()) {
77 String key = m.group(1).trim();
78 String value = m.group(2).trim();
79 if (value.startsWith("[") && value.endsWith("]")) {
80 value = value.substring(1, value.length() - 1);
81 JSONArray arr = new JSONArray();
82 for (String v : value.split(", ?")) {
83 if (!v.isEmpty()) arr.put(v.trim());
84 }
85 obj.put(key, arr);
86 } else {
87 obj.put(key, value);
88 }
89 }
90 Matcher userMatcher = Pattern.compile("User (\\d+)\\n([\\s\\S]+?)(?=\\n\\s*\\w|$)").matcher(section);
91 while (userMatcher.find()) {
92 String userKey = "User " + userMatcher.group(1);
93 String pkgs = userMatcher.group(2);
94 JSONArray pkgArr = new JSONArray();
95 for (String line : pkgs.split("\n")) {
96 line = line.trim();
97 if (!line.isEmpty()) pkgArr.put(line);
98 }
99 obj.put(userKey, pkgArr);
100 }
101 return obj;
102 }
103
104 private JSONArray parsePendingAlarms(String section) {
105 JSONArray alarms = new JSONArray();
106 Pattern alarmStartPattern = Pattern.compile("^\s*(ELAPSED|ELAPSED_WAKEUP|RTC|RTC_WAKEUP) #(\\d+): (Alarm\\{.*?)(?=(^\\s*(ELAPSED|ELAPSED_WAKEUP|RTC|RTC_WAKEUP) #(\\d+):)|(\\s{2}))", Pattern.MULTILINE | Pattern.DOTALL);
107 Matcher m = alarmStartPattern.matcher(section);
108 while (m.find()) {
109 JSONObject alarm = new JSONObject();
110 alarm.put("type", m.group(1));
111 alarm.put("number", m.group(2));
112 String details = m.group(3).trim();
113 String[] tmpArray = details.split("\\n", 2);
114 alarm.put("alarmInfo", tmpArray[0]);
115 details = details.replace(tmpArray[0], "");
116 Pattern fieldPattern = Pattern.compile(
117 "tag=([^\n]+)|type=([^\\s]+)|origWhen=([^\\s]+)|window=([^\\s]+)|repeatInterval=([^\\s]+)|count=([^\\s]+)|flags=([^\\s]+)|operation=([^\n]+)|listener=([^\n]+)|idle-options=([^\n]+)",
118 Pattern.MULTILINE);
119 Matcher fieldMatcher = fieldPattern.matcher(details);
120 while (fieldMatcher.find()) {
121 if (fieldMatcher.group(1) != null) alarm.put("tag", fieldMatcher.group(1).trim());
122 if (fieldMatcher.group(2) != null) alarm.put("alarmType", fieldMatcher.group(2).trim());
123 if (fieldMatcher.group(3) != null) alarm.put("origWhen", fieldMatcher.group(3).trim());
124 if (fieldMatcher.group(4) != null) alarm.put("window", fieldMatcher.group(4).trim());
125 if (fieldMatcher.group(5) != null) alarm.put("repeatInterval", fieldMatcher.group(5).trim());
126 if (fieldMatcher.group(6) != null) alarm.put("count", fieldMatcher.group(6).trim());
127 if (fieldMatcher.group(7) != null) alarm.put("flags", fieldMatcher.group(7).trim());
128 if (fieldMatcher.group(8) != null) alarm.put("operation", fieldMatcher.group(8).trim());
129 if (fieldMatcher.group(9) != null) alarm.put("listener", fieldMatcher.group(9).trim());
130 if (fieldMatcher.group(10) != null) alarm.put("idleOptions", fieldMatcher.group(10).trim());
131 }
132 Pattern extraPattern = Pattern.compile(
133 "policyWhenElapsed: ([^\n]+)|whenElapsed=([^\\s]+)|maxWhenElapsed=([^\\s]+)|exactAllowReason=([^\\s]+)|procName ([^\\s]+)|PendingIntent\\{([^:]+): ([^}]+)\\}",
134 Pattern.MULTILINE);
135 Matcher extraMatcher = extraPattern.matcher(details);
136 while (extraMatcher.find()) {
137 if (extraMatcher.group(1) != null) alarm.put("policyWhenElapsed", extraMatcher.group(1).trim());
138 if (extraMatcher.group(2) != null) alarm.put("whenElapsed", extraMatcher.group(2).trim());
139 if (extraMatcher.group(3) != null) alarm.put("maxWhenElapsed", extraMatcher.group(3).trim());
140 if (extraMatcher.group(4) != null) alarm.put("exactAllowReason", extraMatcher.group(4).trim());
141 if (extraMatcher.group(5) != null) alarm.put("procName", extraMatcher.group(5).trim());
142 if (extraMatcher.group(6) != null && extraMatcher.group(7) != null) {
143 alarm.put("pendingIntent", extraMatcher.group(7).trim());
144 }
145 }
146 alarm.put("rawDetails", details);
147 alarms.put(alarm);
148 }
149 return alarms;
150 }
151
152 private JSONObject parseTimeInfoSection(String section) {
153 JSONObject obj = new JSONObject();
154
155 String timeInformation = StringUtils.substringBetween(section, "nowRTC=", "mLastTimeChangeClockTime=").trim().replace(" ", "=");
156 String[] components = timeInformation.split("=");
157
158 String dateTime = components[1] + " " + components[2];
159 Long uptimeMs = Long.valueOf(components[4]);
160
161 long uptimeSeconds = uptimeMs / 1000;
162 long uptimeHours = uptimeSeconds / 3600;
163 long uptimeMinutes = uptimeSeconds % 3600 / 60;
164 long uptimeSecs = uptimeSeconds % 60;
165
166 obj.put("System Uptime(ms)", uptimeMs);
167 obj.put("System Uptime", uptimeHours + ":" + uptimeMinutes + ":" + uptimeSecs);
168 obj.put("Last DateTime", dateTime);
169 return obj;
170 }
171 }