@yume-chan/android-bin 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +6 -0
- package/CHANGELOG.md +6 -1
- package/README.md +15 -0
- package/esm/bu.d.ts +1 -1
- package/esm/bu.d.ts.map +1 -1
- package/esm/bu.js +6 -2
- package/esm/bu.js.map +1 -1
- package/esm/bug-report.d.ts +2 -2
- package/esm/bug-report.d.ts.map +1 -1
- package/esm/bug-report.js +27 -16
- package/esm/bug-report.js.map +1 -1
- package/esm/demo-mode.d.ts +3 -3
- package/esm/demo-mode.d.ts.map +1 -1
- package/esm/demo-mode.js +85 -53
- package/esm/demo-mode.js.map +1 -1
- package/esm/logcat.d.ts +4 -4
- package/esm/logcat.d.ts.map +1 -1
- package/esm/logcat.js +52 -44
- package/esm/logcat.js.map +1 -1
- package/esm/settings.d.ts +2 -2
- package/esm/settings.d.ts.map +1 -1
- package/package.json +12 -9
- package/src/bu.ts +7 -3
- package/src/bug-report.ts +74 -48
- package/src/demo-mode.ts +113 -65
- package/src/logcat.ts +139 -89
- package/tsconfig.build.json +3 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/.rush/temp/package-deps_build.json +0 -17
- package/.rush/temp/package-deps_build_watch.json +0 -17
- package/.rush/temp/shrinkwrap-deps.json +0 -5
- package/android-bin.build.log +0 -1
- package/tsconfig.json +0 -11
package/src/demo-mode.ts
CHANGED
|
@@ -4,28 +4,51 @@
|
|
|
4
4
|
// cspell: ignore systemui
|
|
5
5
|
// cspell: ignore sysui
|
|
6
6
|
|
|
7
|
-
import { Adb, AdbCommandBase } from
|
|
7
|
+
import { type Adb, AdbCommandBase } from "@yume-chan/adb";
|
|
8
|
+
|
|
8
9
|
import { Settings } from "./settings.js";
|
|
9
10
|
|
|
10
11
|
export enum DemoModeSignalStrength {
|
|
11
|
-
Hidden =
|
|
12
|
-
Level0 =
|
|
13
|
-
Level1 =
|
|
14
|
-
Level2 =
|
|
15
|
-
Level3 =
|
|
16
|
-
Level4 =
|
|
12
|
+
Hidden = "null",
|
|
13
|
+
Level0 = "0",
|
|
14
|
+
Level1 = "1",
|
|
15
|
+
Level2 = "2",
|
|
16
|
+
Level3 = "3",
|
|
17
|
+
Level4 = "4",
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java;l=1362;drc=3b775bf7ad89902f94e03d191b0d8fbdebf2bdbf
|
|
20
|
-
export const DemoModeMobileDataTypes = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export const DemoModeMobileDataTypes = [
|
|
22
|
+
"1x",
|
|
23
|
+
"3g",
|
|
24
|
+
"4g",
|
|
25
|
+
"4g+",
|
|
26
|
+
"5g",
|
|
27
|
+
"5ge",
|
|
28
|
+
"5g+",
|
|
29
|
+
"e",
|
|
30
|
+
"g",
|
|
31
|
+
"h",
|
|
32
|
+
"h+",
|
|
33
|
+
"lte",
|
|
34
|
+
"lte+",
|
|
35
|
+
"dis",
|
|
36
|
+
"not",
|
|
37
|
+
"null",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
export type DemoModeMobileDataType = typeof DemoModeMobileDataTypes[number];
|
|
24
41
|
|
|
25
42
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java;l=3136
|
|
26
|
-
export const DemoModeStatusBarModes = [
|
|
43
|
+
export const DemoModeStatusBarModes = [
|
|
44
|
+
"opaque",
|
|
45
|
+
"translucent",
|
|
46
|
+
"semi-transparent",
|
|
47
|
+
"transparent",
|
|
48
|
+
"warning",
|
|
49
|
+
] as const;
|
|
27
50
|
|
|
28
|
-
export type DemoModeStatusBarMode =
|
|
51
|
+
export type DemoModeStatusBarMode = typeof DemoModeStatusBarModes[number];
|
|
29
52
|
|
|
30
53
|
export class DemoMode extends AdbCommandBase {
|
|
31
54
|
private settings: Settings;
|
|
@@ -35,145 +58,170 @@ export class DemoMode extends AdbCommandBase {
|
|
|
35
58
|
this.settings = new Settings(adb);
|
|
36
59
|
}
|
|
37
60
|
|
|
38
|
-
public static readonly AllowedSettingKey =
|
|
61
|
+
public static readonly AllowedSettingKey = "sysui_demo_allowed";
|
|
39
62
|
|
|
40
63
|
// Demo Mode actually doesn't have a setting indicates its enablement
|
|
41
64
|
// However Developer Mode menu uses this key
|
|
42
65
|
// So we can only try our best to guess if it's enabled
|
|
43
|
-
public static readonly EnabledSettingKey =
|
|
66
|
+
public static readonly EnabledSettingKey = "sysui_tuner_demo_on";
|
|
44
67
|
|
|
45
68
|
public async getAllowed(): Promise<boolean> {
|
|
46
|
-
const output = await this.settings.get(
|
|
47
|
-
|
|
69
|
+
const output = await this.settings.get(
|
|
70
|
+
"global",
|
|
71
|
+
DemoMode.AllowedSettingKey
|
|
72
|
+
);
|
|
73
|
+
return output.trim() === "1";
|
|
48
74
|
}
|
|
49
75
|
|
|
50
76
|
public async setAllowed(value: boolean): Promise<void> {
|
|
51
77
|
if (value) {
|
|
52
|
-
await this.settings.put(
|
|
78
|
+
await this.settings.put("global", DemoMode.AllowedSettingKey, "1");
|
|
53
79
|
} else {
|
|
54
80
|
await this.setEnabled(false);
|
|
55
|
-
await this.settings.delete(
|
|
81
|
+
await this.settings.delete("global", DemoMode.AllowedSettingKey);
|
|
56
82
|
}
|
|
57
83
|
}
|
|
58
84
|
|
|
59
85
|
public async getEnabled(): Promise<boolean> {
|
|
60
|
-
const result = await this.settings.get(
|
|
61
|
-
|
|
86
|
+
const result = await this.settings.get(
|
|
87
|
+
"global",
|
|
88
|
+
DemoMode.EnabledSettingKey
|
|
89
|
+
);
|
|
90
|
+
return result.trim() === "1";
|
|
62
91
|
}
|
|
63
92
|
|
|
64
93
|
public async setEnabled(value: boolean): Promise<void> {
|
|
65
94
|
if (value) {
|
|
66
|
-
await this.settings.put(
|
|
95
|
+
await this.settings.put("global", DemoMode.EnabledSettingKey, "1");
|
|
67
96
|
} else {
|
|
68
|
-
await this.settings.delete(
|
|
69
|
-
await this.broadcast(
|
|
97
|
+
await this.settings.delete("global", DemoMode.EnabledSettingKey);
|
|
98
|
+
await this.broadcast("exit");
|
|
70
99
|
}
|
|
71
100
|
}
|
|
72
101
|
|
|
73
|
-
public async broadcast(
|
|
102
|
+
public async broadcast(
|
|
103
|
+
command: string,
|
|
104
|
+
extra?: Record<string, string>
|
|
105
|
+
): Promise<void> {
|
|
74
106
|
await this.adb.subprocess.spawnAndWaitLegacy([
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
"am",
|
|
108
|
+
"broadcast",
|
|
109
|
+
"-a",
|
|
110
|
+
"com.android.systemui.demo",
|
|
111
|
+
"-e",
|
|
112
|
+
"command",
|
|
81
113
|
command,
|
|
82
|
-
...(extra
|
|
114
|
+
...(extra
|
|
115
|
+
? Object.entries(extra).flatMap(([key, value]) => [
|
|
116
|
+
"-e",
|
|
117
|
+
key,
|
|
118
|
+
value,
|
|
119
|
+
])
|
|
120
|
+
: []),
|
|
83
121
|
]);
|
|
84
122
|
}
|
|
85
123
|
|
|
86
124
|
public async setBatteryLevel(level: number): Promise<void> {
|
|
87
|
-
await this.broadcast(
|
|
125
|
+
await this.broadcast("battery", { level: level.toString() });
|
|
88
126
|
}
|
|
89
127
|
|
|
90
128
|
public async setBatteryCharging(value: boolean): Promise<void> {
|
|
91
|
-
await this.broadcast(
|
|
129
|
+
await this.broadcast("battery", { plugged: value.toString() });
|
|
92
130
|
}
|
|
93
131
|
|
|
94
132
|
public async setPowerSaveMode(value: boolean): Promise<void> {
|
|
95
|
-
await this.broadcast(
|
|
133
|
+
await this.broadcast("battery", { powersave: value.toString() });
|
|
96
134
|
}
|
|
97
135
|
|
|
98
136
|
public async setAirplaneMode(show: boolean): Promise<void> {
|
|
99
|
-
await this.broadcast(
|
|
137
|
+
await this.broadcast("network", { airplane: show ? "show" : "hide" });
|
|
100
138
|
}
|
|
101
139
|
|
|
102
|
-
public async setWifiSignalStrength(
|
|
103
|
-
|
|
140
|
+
public async setWifiSignalStrength(
|
|
141
|
+
value: DemoModeSignalStrength
|
|
142
|
+
): Promise<void> {
|
|
143
|
+
await this.broadcast("network", { wifi: "show", level: value });
|
|
104
144
|
}
|
|
105
145
|
|
|
106
|
-
public async setMobileDataType(
|
|
146
|
+
public async setMobileDataType(
|
|
147
|
+
value: DemoModeMobileDataType
|
|
148
|
+
): Promise<void> {
|
|
107
149
|
for (let i = 0; i < 2; i += 1) {
|
|
108
|
-
await this.broadcast(
|
|
109
|
-
mobile:
|
|
110
|
-
sims:
|
|
111
|
-
nosim:
|
|
112
|
-
slot:
|
|
150
|
+
await this.broadcast("network", {
|
|
151
|
+
mobile: "show",
|
|
152
|
+
sims: "1",
|
|
153
|
+
nosim: "hide",
|
|
154
|
+
slot: "0",
|
|
113
155
|
datatype: value,
|
|
114
|
-
fully:
|
|
115
|
-
roam:
|
|
116
|
-
level:
|
|
117
|
-
inflate:
|
|
118
|
-
activity:
|
|
119
|
-
carriernetworkchange:
|
|
156
|
+
fully: "true",
|
|
157
|
+
roam: "false",
|
|
158
|
+
level: "4",
|
|
159
|
+
inflate: "false",
|
|
160
|
+
activity: "in",
|
|
161
|
+
carriernetworkchange: "hide",
|
|
120
162
|
});
|
|
121
163
|
}
|
|
122
164
|
}
|
|
123
165
|
|
|
124
|
-
public async setMobileSignalStrength(
|
|
125
|
-
|
|
166
|
+
public async setMobileSignalStrength(
|
|
167
|
+
value: DemoModeSignalStrength
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
await this.broadcast("network", { mobile: "show", level: value });
|
|
126
170
|
}
|
|
127
171
|
|
|
128
172
|
public async setNoSimCardIcon(show: boolean): Promise<void> {
|
|
129
|
-
await this.broadcast(
|
|
173
|
+
await this.broadcast("network", { nosim: show ? "show" : "hide" });
|
|
130
174
|
}
|
|
131
175
|
|
|
132
176
|
public async setStatusBarMode(mode: DemoModeStatusBarMode): Promise<void> {
|
|
133
|
-
await this.broadcast(
|
|
177
|
+
await this.broadcast("bars", { mode });
|
|
134
178
|
}
|
|
135
179
|
|
|
136
180
|
public async setVibrateModeEnabled(value: boolean): Promise<void> {
|
|
137
181
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java;l=103
|
|
138
|
-
await this.broadcast(
|
|
182
|
+
await this.broadcast("status", { volume: value ? "vibrate" : "hide" });
|
|
139
183
|
}
|
|
140
184
|
|
|
141
185
|
public async setBluetoothConnected(value: boolean): Promise<void> {
|
|
142
186
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java;l=114
|
|
143
|
-
await this.broadcast(
|
|
187
|
+
await this.broadcast("status", {
|
|
188
|
+
bluetooth: value ? "connected" : "hide",
|
|
189
|
+
});
|
|
144
190
|
}
|
|
145
191
|
|
|
146
192
|
public async setLocatingIcon(show: boolean): Promise<void> {
|
|
147
|
-
await this.broadcast(
|
|
193
|
+
await this.broadcast("status", { location: show ? "show" : "hide" });
|
|
148
194
|
}
|
|
149
195
|
|
|
150
196
|
public async setAlarmIcon(show: boolean): Promise<void> {
|
|
151
|
-
await this.broadcast(
|
|
197
|
+
await this.broadcast("status", { alarm: show ? "show" : "hide" });
|
|
152
198
|
}
|
|
153
199
|
|
|
154
200
|
public async setSyncingIcon(show: boolean): Promise<void> {
|
|
155
|
-
await this.broadcast(
|
|
201
|
+
await this.broadcast("status", { sync: show ? "show" : "hide" });
|
|
156
202
|
}
|
|
157
203
|
|
|
158
204
|
public async setMuteIcon(show: boolean): Promise<void> {
|
|
159
|
-
await this.broadcast(
|
|
205
|
+
await this.broadcast("status", { mute: show ? "show" : "hide" });
|
|
160
206
|
}
|
|
161
207
|
|
|
162
208
|
public async setSpeakerPhoneIcon(show: boolean): Promise<void> {
|
|
163
|
-
await this.broadcast(
|
|
209
|
+
await this.broadcast("status", {
|
|
210
|
+
speakerphone: show ? "show" : "hide",
|
|
211
|
+
});
|
|
164
212
|
}
|
|
165
213
|
|
|
166
214
|
public async setNotificationsVisibility(show: boolean): Promise<void> {
|
|
167
215
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java;l=3131
|
|
168
|
-
await this.broadcast(
|
|
216
|
+
await this.broadcast("notifications", { visible: show.toString() });
|
|
169
217
|
}
|
|
170
218
|
|
|
171
219
|
public async setTime(hour: number, minute: number): Promise<void> {
|
|
172
|
-
await this.broadcast(
|
|
220
|
+
await this.broadcast("clock", {
|
|
173
221
|
// cspell: disable-next-line
|
|
174
222
|
hhmm:
|
|
175
|
-
hour.toString().padStart(2,
|
|
176
|
-
minute.toString().padStart(2,
|
|
223
|
+
hour.toString().padStart(2, "0") +
|
|
224
|
+
minute.toString().padStart(2, "0"),
|
|
177
225
|
});
|
|
178
226
|
}
|
|
179
227
|
}
|
package/src/logcat.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
// cspell: ignore logcat
|
|
2
2
|
|
|
3
|
-
import { AdbCommandBase, AdbSubprocessNoneProtocol } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
|
|
4
|
+
import {
|
|
5
|
+
BufferedTransformStream,
|
|
6
|
+
DecodeUtf8Stream,
|
|
7
|
+
SplitStringStream,
|
|
8
|
+
WrapReadableStream,
|
|
9
|
+
WritableStream,
|
|
10
|
+
type ReadableStream,
|
|
11
|
+
} from "@yume-chan/stream-extra";
|
|
12
|
+
import Struct, {
|
|
13
|
+
decodeUtf8,
|
|
14
|
+
type StructAsyncDeserializeStream,
|
|
15
|
+
} from "@yume-chan/struct";
|
|
6
16
|
|
|
7
17
|
// `adb logcat` is an alias to `adb shell logcat`
|
|
8
18
|
// so instead of adding to core library, it's implemented here
|
|
@@ -34,17 +44,18 @@ export enum AndroidLogPriority {
|
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=140;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
|
|
37
|
-
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
|
48
|
+
{
|
|
49
|
+
[AndroidLogPriority.Unknown]: "?",
|
|
50
|
+
[AndroidLogPriority.Default]: "?",
|
|
51
|
+
[AndroidLogPriority.Verbose]: "V",
|
|
52
|
+
[AndroidLogPriority.Debug]: "D",
|
|
53
|
+
[AndroidLogPriority.Info]: "I",
|
|
54
|
+
[AndroidLogPriority.Warn]: "W",
|
|
55
|
+
[AndroidLogPriority.Error]: "E",
|
|
56
|
+
[AndroidLogPriority.Fatal]: "F",
|
|
57
|
+
[AndroidLogPriority.Silent]: "S",
|
|
58
|
+
};
|
|
48
59
|
|
|
49
60
|
export enum LogcatFormat {
|
|
50
61
|
Brief,
|
|
@@ -54,7 +65,7 @@ export enum LogcatFormat {
|
|
|
54
65
|
Raw,
|
|
55
66
|
Time,
|
|
56
67
|
ThreadTime,
|
|
57
|
-
Long
|
|
68
|
+
Long,
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
export interface LogcatFormatModifiers {
|
|
@@ -76,23 +87,25 @@ export interface LogcatOptions {
|
|
|
76
87
|
const NANOSECONDS_PER_SECOND = BigInt(1e9);
|
|
77
88
|
|
|
78
89
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
|
79
|
-
export const LoggerEntry =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
export const LoggerEntry = new Struct({ littleEndian: true })
|
|
91
|
+
.uint16("payloadSize")
|
|
92
|
+
.uint16("headerSize")
|
|
93
|
+
.int32("pid")
|
|
94
|
+
.uint32("tid")
|
|
95
|
+
.uint32("second")
|
|
96
|
+
.uint32("nanoseconds")
|
|
97
|
+
.uint32("logId")
|
|
98
|
+
.uint32("uid")
|
|
99
|
+
.extra({
|
|
100
|
+
get timestamp() {
|
|
101
|
+
return (
|
|
102
|
+
BigInt(this.second) * NANOSECONDS_PER_SECOND +
|
|
103
|
+
BigInt(this.nanoseconds)
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export type LoggerEntry = typeof LoggerEntry["TDeserializeResult"];
|
|
96
109
|
|
|
97
110
|
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
|
|
98
111
|
export interface AndroidLogEntry extends LoggerEntry {
|
|
@@ -107,24 +120,28 @@ export function formatAndroidLogEntry(
|
|
|
107
120
|
format: LogcatFormat = LogcatFormat.Brief,
|
|
108
121
|
modifier?: LogcatFormatModifiers
|
|
109
122
|
) {
|
|
110
|
-
const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` :
|
|
123
|
+
const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` : "";
|
|
111
124
|
|
|
112
125
|
switch (format) {
|
|
113
126
|
// TODO: implement other formats
|
|
114
127
|
default:
|
|
115
|
-
return `${
|
|
128
|
+
return `${
|
|
129
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
130
|
+
}/${entry.tag.padEnd(8)}(${uid}${entry.pid
|
|
131
|
+
.toString()
|
|
132
|
+
.padStart(5)}): ${entry.message}`;
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
|
|
119
136
|
function findTagEnd(payload: Uint8Array) {
|
|
120
|
-
for (const separator of [0,
|
|
137
|
+
for (const separator of [0, " ".charCodeAt(0), ":".charCodeAt(0)]) {
|
|
121
138
|
const index = payload.indexOf(separator);
|
|
122
139
|
if (index !== -1) {
|
|
123
140
|
return index;
|
|
124
141
|
}
|
|
125
142
|
}
|
|
126
143
|
|
|
127
|
-
const index = payload.findIndex(x => x >= 0x7f);
|
|
144
|
+
const index = payload.findIndex((x) => x >= 0x7f);
|
|
128
145
|
if (index !== -1) {
|
|
129
146
|
return index;
|
|
130
147
|
}
|
|
@@ -132,8 +149,12 @@ function findTagEnd(payload: Uint8Array) {
|
|
|
132
149
|
return payload.length;
|
|
133
150
|
}
|
|
134
151
|
|
|
135
|
-
export async function deserializeAndroidLogEntry(
|
|
136
|
-
|
|
152
|
+
export async function deserializeAndroidLogEntry(
|
|
153
|
+
stream: StructAsyncDeserializeStream
|
|
154
|
+
): Promise<AndroidLogEntry> {
|
|
155
|
+
const entry = (await LoggerEntry.deserialize(
|
|
156
|
+
stream
|
|
157
|
+
)) as unknown as AndroidLogEntry;
|
|
137
158
|
if (entry.headerSize !== LoggerEntry.size) {
|
|
138
159
|
await stream.read(entry.headerSize - LoggerEntry.size);
|
|
139
160
|
}
|
|
@@ -146,7 +167,10 @@ export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeS
|
|
|
146
167
|
payload = payload.subarray(1);
|
|
147
168
|
const tagEnd = findTagEnd(payload);
|
|
148
169
|
entry.tag = decodeUtf8(payload.subarray(0, tagEnd));
|
|
149
|
-
entry.message =
|
|
170
|
+
entry.message =
|
|
171
|
+
tagEnd < payload.length - 1
|
|
172
|
+
? decodeUtf8(payload.subarray(tagEnd + 1))
|
|
173
|
+
: "";
|
|
150
174
|
return entry;
|
|
151
175
|
}
|
|
152
176
|
|
|
@@ -166,72 +190,91 @@ export class Logcat extends AdbCommandBase {
|
|
|
166
190
|
|
|
167
191
|
public static logNameToId(name: string): LogId {
|
|
168
192
|
const key = name[0]!.toUpperCase() + name.substring(1);
|
|
169
|
-
return
|
|
193
|
+
return LogId[key as keyof typeof LogId];
|
|
170
194
|
}
|
|
171
195
|
|
|
172
196
|
public static joinLogId(ids: LogId[]): string {
|
|
173
|
-
return ids.map(id => Logcat.logIdToName(id)).join(
|
|
197
|
+
return ids.map((id) => Logcat.logIdToName(id)).join(",");
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
public static parseSize(value: number, multiplier: string): number {
|
|
177
|
-
const MULTIPLIERS = [
|
|
201
|
+
const MULTIPLIERS = ["", "Ki", "Mi", "Gi"];
|
|
178
202
|
return value * 1024 ** (MULTIPLIERS.indexOf(multiplier) || 0);
|
|
179
203
|
}
|
|
180
204
|
|
|
181
205
|
// TODO: logcat: Support output format before Android 10
|
|
182
206
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/748128
|
|
183
|
-
public static readonly LOG_SIZE_REGEX_10 =
|
|
207
|
+
public static readonly LOG_SIZE_REGEX_10 =
|
|
208
|
+
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
|
|
184
209
|
|
|
185
210
|
// Android 11 added `readable` part
|
|
186
211
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/1390940
|
|
187
|
-
public static readonly LOG_SIZE_REGEX_11 =
|
|
212
|
+
public static readonly LOG_SIZE_REGEX_11 =
|
|
213
|
+
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
|
|
188
214
|
|
|
189
215
|
public async getLogSize(ids?: LogId[]): Promise<LogSize[]> {
|
|
190
216
|
const { stdout } = await this.adb.subprocess.spawn([
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
...(ids ? [
|
|
217
|
+
"logcat",
|
|
218
|
+
"-g",
|
|
219
|
+
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
|
194
220
|
]);
|
|
195
221
|
|
|
196
222
|
const result: LogSize[] = [];
|
|
197
223
|
await stdout
|
|
198
224
|
.pipeThrough(new DecodeUtf8Stream())
|
|
199
|
-
.pipeThrough(new SplitStringStream(
|
|
200
|
-
.pipeTo(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
.pipeThrough(new SplitStringStream("\n"))
|
|
226
|
+
.pipeTo(
|
|
227
|
+
new WritableStream({
|
|
228
|
+
write(chunk) {
|
|
229
|
+
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
|
230
|
+
if (match) {
|
|
231
|
+
result.push({
|
|
232
|
+
id: Logcat.logNameToId(match[1]!),
|
|
233
|
+
size: Logcat.parseSize(
|
|
234
|
+
Number.parseInt(match[2]!, 10),
|
|
235
|
+
match[3]!
|
|
236
|
+
),
|
|
237
|
+
readable: Logcat.parseSize(
|
|
238
|
+
Number.parseInt(match[6]!, 10),
|
|
239
|
+
match[7]!
|
|
240
|
+
),
|
|
241
|
+
consumed: Logcat.parseSize(
|
|
242
|
+
Number.parseInt(match[4]!, 10),
|
|
243
|
+
match[5]!
|
|
244
|
+
),
|
|
245
|
+
maxEntrySize: parseInt(match[8]!, 10),
|
|
246
|
+
maxPayloadSize: parseInt(match[9]!, 10),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
|
|
251
|
+
if (match) {
|
|
252
|
+
result.push({
|
|
253
|
+
id: Logcat.logNameToId(match[1]!),
|
|
254
|
+
size: Logcat.parseSize(
|
|
255
|
+
Number.parseInt(match[2]!, 10),
|
|
256
|
+
match[3]!
|
|
257
|
+
),
|
|
258
|
+
consumed: Logcat.parseSize(
|
|
259
|
+
Number.parseInt(match[4]!, 10),
|
|
260
|
+
match[5]!
|
|
261
|
+
),
|
|
262
|
+
maxEntrySize: parseInt(match[6]!, 10),
|
|
263
|
+
maxPayloadSize: parseInt(match[7]!, 10),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
);
|
|
226
269
|
|
|
227
270
|
return result;
|
|
228
271
|
}
|
|
229
272
|
|
|
230
273
|
public async clear(ids?: LogId[]) {
|
|
231
274
|
await this.adb.subprocess.spawnAndWait([
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
...(ids ? [
|
|
275
|
+
"logcat",
|
|
276
|
+
"-c",
|
|
277
|
+
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
|
235
278
|
]);
|
|
236
279
|
}
|
|
237
280
|
|
|
@@ -239,18 +282,25 @@ export class Logcat extends AdbCommandBase {
|
|
|
239
282
|
return new WrapReadableStream(async () => {
|
|
240
283
|
// TODO: make `spawn` return synchronously with streams pending
|
|
241
284
|
// so it's easier to chain them.
|
|
242
|
-
const { stdout } = await this.adb.subprocess.spawn(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
285
|
+
const { stdout } = await this.adb.subprocess.spawn(
|
|
286
|
+
[
|
|
287
|
+
"logcat",
|
|
288
|
+
"-B",
|
|
289
|
+
...(options?.pid ? ["--pid", options.pid.toString()] : []),
|
|
290
|
+
...(options?.ids
|
|
291
|
+
? ["-b", Logcat.joinLogId(options.ids)]
|
|
292
|
+
: []),
|
|
293
|
+
],
|
|
294
|
+
{
|
|
295
|
+
// PERF: None protocol is 150% faster then Shell protocol
|
|
296
|
+
protocols: [AdbSubprocessNoneProtocol],
|
|
297
|
+
}
|
|
298
|
+
);
|
|
251
299
|
return stdout;
|
|
252
|
-
}).pipeThrough(
|
|
253
|
-
|
|
254
|
-
|
|
300
|
+
}).pipeThrough(
|
|
301
|
+
new BufferedTransformStream((stream) => {
|
|
302
|
+
return deserializeAndroidLogEntry(stream);
|
|
303
|
+
})
|
|
304
|
+
);
|
|
255
305
|
}
|
|
256
306
|
}
|