@yume-chan/android-bin 0.0.15 → 0.0.17
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/.rush/temp/package-deps_build.json +8 -8
- package/.rush/temp/package-deps_build_watch.json +8 -8
- package/.rush/temp/shrinkwrap-deps.json +3 -2
- package/CHANGELOG.json +27 -0
- package/CHANGELOG.md +16 -1
- package/README.md +5 -5
- package/android-bin.build.log +0 -1
- package/esm/bug-report.d.ts +2 -1
- package/esm/bug-report.d.ts.map +1 -1
- package/esm/bug-report.js +3 -2
- package/esm/bug-report.js.map +1 -1
- package/esm/demo-mode.js +1 -1
- package/esm/demo-mode.js.map +1 -1
- package/esm/logcat.d.ts +38 -13
- package/esm/logcat.d.ts.map +1 -1
- package/esm/logcat.js +91 -47
- package/esm/logcat.js.map +1 -1
- package/package.json +8 -8
- package/src/bu.ts +22 -22
- package/src/bug-report.ts +156 -155
- package/src/demo-mode.ts +179 -179
- package/src/index.ts +6 -6
- package/src/logcat.ts +111 -42
- package/src/settings.ts +49 -49
- package/tsconfig.json +11 -8
- package/.rush/temp/package-deps_build +0 -0
- package/.rush/temp/package-deps_postinstall.json +0 -13
package/src/demo-mode.ts
CHANGED
|
@@ -1,179 +1,179 @@
|
|
|
1
|
-
// cspell: ignore carriernetworkchange
|
|
2
|
-
// cspell: ignore powersave
|
|
3
|
-
// cspell: ignore nosim
|
|
4
|
-
// cspell: ignore systemui
|
|
5
|
-
// cspell: ignore sysui
|
|
6
|
-
|
|
7
|
-
import { Adb, AdbCommandBase } from '@yume-chan/adb';
|
|
8
|
-
import { Settings } from "./settings.js";
|
|
9
|
-
|
|
10
|
-
export enum DemoModeSignalStrength {
|
|
11
|
-
Hidden = 'null',
|
|
12
|
-
Level0 = '0',
|
|
13
|
-
Level1 = '1',
|
|
14
|
-
Level2 = '2',
|
|
15
|
-
Level3 = '3',
|
|
16
|
-
Level4 = '4',
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/
|
|
20
|
-
export const DemoModeMobileDataTypes = ['1x', '3g', '4g', '4g+', '5g', '5ge', '5g+',
|
|
21
|
-
'e', 'g', 'h', 'h+', 'lte', 'lte+', 'dis', 'not', 'null'] as const;
|
|
22
|
-
|
|
23
|
-
export type DemoModeMobileDataType = (typeof DemoModeMobileDataTypes)[number];
|
|
24
|
-
|
|
25
|
-
// 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 = ['opaque', 'translucent', 'semi-transparent', 'transparent', 'warning'] as const;
|
|
27
|
-
|
|
28
|
-
export type DemoModeStatusBarMode = (typeof DemoModeStatusBarModes)[number];
|
|
29
|
-
|
|
30
|
-
export class DemoMode extends AdbCommandBase {
|
|
31
|
-
private settings: Settings;
|
|
32
|
-
|
|
33
|
-
constructor(adb: Adb) {
|
|
34
|
-
super(adb);
|
|
35
|
-
this.settings = new Settings(adb);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public static readonly AllowedSettingKey = 'sysui_demo_allowed';
|
|
39
|
-
|
|
40
|
-
// Demo Mode actually doesn't have a setting indicates its enablement
|
|
41
|
-
// However Developer Mode menu uses this key
|
|
42
|
-
// So we can only try our best to guess if it's enabled
|
|
43
|
-
public static readonly EnabledSettingKey = 'sysui_tuner_demo_on';
|
|
44
|
-
|
|
45
|
-
public async getAllowed(): Promise<boolean> {
|
|
46
|
-
const output = await this.settings.get('global', DemoMode.AllowedSettingKey);
|
|
47
|
-
return output.trim() === '1';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public async setAllowed(value: boolean): Promise<void> {
|
|
51
|
-
if (value) {
|
|
52
|
-
await this.settings.put('global', DemoMode.AllowedSettingKey, '1');
|
|
53
|
-
} else {
|
|
54
|
-
await this.setEnabled(false);
|
|
55
|
-
await this.settings.delete('global', DemoMode.AllowedSettingKey);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public async getEnabled(): Promise<boolean> {
|
|
60
|
-
const result = await this.settings.get('global', DemoMode.EnabledSettingKey);
|
|
61
|
-
return result.trim() === '1';
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public async setEnabled(value: boolean): Promise<void> {
|
|
65
|
-
if (value) {
|
|
66
|
-
await this.settings.put('global', DemoMode.EnabledSettingKey, '1');
|
|
67
|
-
} else {
|
|
68
|
-
await this.settings.delete('global', DemoMode.EnabledSettingKey);
|
|
69
|
-
await this.broadcast('exit');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public async broadcast(command: string, extra?: Record<string, string>): Promise<void> {
|
|
74
|
-
await this.adb.subprocess.spawnAndWaitLegacy([
|
|
75
|
-
'am',
|
|
76
|
-
'broadcast',
|
|
77
|
-
'-a',
|
|
78
|
-
'com.android.systemui.demo',
|
|
79
|
-
'-e',
|
|
80
|
-
'command',
|
|
81
|
-
command,
|
|
82
|
-
...(extra ? Object.entries(extra).flatMap(([key, value]) => ['-e', key, value]) : []),
|
|
83
|
-
]);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
public async setBatteryLevel(level: number): Promise<void> {
|
|
87
|
-
await this.broadcast('battery', { level: level.toString() });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public async setBatteryCharging(value: boolean): Promise<void> {
|
|
91
|
-
await this.broadcast('battery', { plugged: value.toString() });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public async setPowerSaveMode(value: boolean): Promise<void> {
|
|
95
|
-
await this.broadcast('battery', { powersave: value.toString() });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public async setAirplaneMode(show: boolean): Promise<void> {
|
|
99
|
-
await this.broadcast('network', { airplane: show ? 'show' : 'hide' });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public async setWifiSignalStrength(value: DemoModeSignalStrength): Promise<void> {
|
|
103
|
-
await this.broadcast('network', { wifi: 'show', level: value });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public async setMobileDataType(value: DemoModeMobileDataType): Promise<void> {
|
|
107
|
-
for (let i = 0; i < 2; i += 1) {
|
|
108
|
-
await this.broadcast('network', {
|
|
109
|
-
mobile: 'show',
|
|
110
|
-
sims: '1',
|
|
111
|
-
nosim: 'hide',
|
|
112
|
-
slot: '0',
|
|
113
|
-
datatype: value,
|
|
114
|
-
fully: 'true',
|
|
115
|
-
roam: 'false',
|
|
116
|
-
level: '4',
|
|
117
|
-
inflate: 'false',
|
|
118
|
-
activity: 'in',
|
|
119
|
-
carriernetworkchange: 'hide',
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public async setMobileSignalStrength(value: DemoModeSignalStrength): Promise<void> {
|
|
125
|
-
await this.broadcast('network', { mobile: 'show', level: value });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public async setNoSimCardIcon(show: boolean): Promise<void> {
|
|
129
|
-
await this.broadcast('network', { nosim: show ? 'show' : 'hide' });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
public async setStatusBarMode(mode: DemoModeStatusBarMode): Promise<void> {
|
|
133
|
-
await this.broadcast('bars', { mode });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public async setVibrateModeEnabled(value: boolean): Promise<void> {
|
|
137
|
-
// 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('status', { volume: value ? 'vibrate' : 'hide' });
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public async setBluetoothConnected(value: boolean): Promise<void> {
|
|
142
|
-
// 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('status', { bluetooth: value ? 'connected' : 'hide' });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
public async setLocatingIcon(show: boolean): Promise<void> {
|
|
147
|
-
await this.broadcast('status', { location: show ? 'show' : 'hide' });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public async setAlarmIcon(show: boolean): Promise<void> {
|
|
151
|
-
await this.broadcast('status', { alarm: show ? 'show' : 'hide' });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
public async setSyncingIcon(show: boolean): Promise<void> {
|
|
155
|
-
await this.broadcast('status', { sync: show ? 'show' : 'hide' });
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public async setMuteIcon(show: boolean): Promise<void> {
|
|
159
|
-
await this.broadcast('status', { mute: show ? 'show' : 'hide' });
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
public async setSpeakerPhoneIcon(show: boolean): Promise<void> {
|
|
163
|
-
await this.broadcast('status', { speakerphone: show ? 'show' : 'hide' });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public async setNotificationsVisibility(show: boolean): Promise<void> {
|
|
167
|
-
// 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('notifications', { visible: show.toString() });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
public async setTime(hour: number, minute: number): Promise<void> {
|
|
172
|
-
await this.broadcast('clock', {
|
|
173
|
-
// cspell: disable-next-line
|
|
174
|
-
hhmm:
|
|
175
|
-
hour.toString().padStart(2, '0') +
|
|
176
|
-
minute.toString().padStart(2, '0'),
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
1
|
+
// cspell: ignore carriernetworkchange
|
|
2
|
+
// cspell: ignore powersave
|
|
3
|
+
// cspell: ignore nosim
|
|
4
|
+
// cspell: ignore systemui
|
|
5
|
+
// cspell: ignore sysui
|
|
6
|
+
|
|
7
|
+
import { Adb, AdbCommandBase } from '@yume-chan/adb';
|
|
8
|
+
import { Settings } from "./settings.js";
|
|
9
|
+
|
|
10
|
+
export enum DemoModeSignalStrength {
|
|
11
|
+
Hidden = 'null',
|
|
12
|
+
Level0 = '0',
|
|
13
|
+
Level1 = '1',
|
|
14
|
+
Level2 = '2',
|
|
15
|
+
Level3 = '3',
|
|
16
|
+
Level4 = '4',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 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 = ['1x', '3g', '4g', '4g+', '5g', '5ge', '5g+',
|
|
21
|
+
'e', 'g', 'h', 'h+', 'lte', 'lte+', 'dis', 'not', 'null'] as const;
|
|
22
|
+
|
|
23
|
+
export type DemoModeMobileDataType = (typeof DemoModeMobileDataTypes)[number];
|
|
24
|
+
|
|
25
|
+
// 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 = ['opaque', 'translucent', 'semi-transparent', 'transparent', 'warning'] as const;
|
|
27
|
+
|
|
28
|
+
export type DemoModeStatusBarMode = (typeof DemoModeStatusBarModes)[number];
|
|
29
|
+
|
|
30
|
+
export class DemoMode extends AdbCommandBase {
|
|
31
|
+
private settings: Settings;
|
|
32
|
+
|
|
33
|
+
constructor(adb: Adb) {
|
|
34
|
+
super(adb);
|
|
35
|
+
this.settings = new Settings(adb);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static readonly AllowedSettingKey = 'sysui_demo_allowed';
|
|
39
|
+
|
|
40
|
+
// Demo Mode actually doesn't have a setting indicates its enablement
|
|
41
|
+
// However Developer Mode menu uses this key
|
|
42
|
+
// So we can only try our best to guess if it's enabled
|
|
43
|
+
public static readonly EnabledSettingKey = 'sysui_tuner_demo_on';
|
|
44
|
+
|
|
45
|
+
public async getAllowed(): Promise<boolean> {
|
|
46
|
+
const output = await this.settings.get('global', DemoMode.AllowedSettingKey);
|
|
47
|
+
return output.trim() === '1';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public async setAllowed(value: boolean): Promise<void> {
|
|
51
|
+
if (value) {
|
|
52
|
+
await this.settings.put('global', DemoMode.AllowedSettingKey, '1');
|
|
53
|
+
} else {
|
|
54
|
+
await this.setEnabled(false);
|
|
55
|
+
await this.settings.delete('global', DemoMode.AllowedSettingKey);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async getEnabled(): Promise<boolean> {
|
|
60
|
+
const result = await this.settings.get('global', DemoMode.EnabledSettingKey);
|
|
61
|
+
return result.trim() === '1';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public async setEnabled(value: boolean): Promise<void> {
|
|
65
|
+
if (value) {
|
|
66
|
+
await this.settings.put('global', DemoMode.EnabledSettingKey, '1');
|
|
67
|
+
} else {
|
|
68
|
+
await this.settings.delete('global', DemoMode.EnabledSettingKey);
|
|
69
|
+
await this.broadcast('exit');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async broadcast(command: string, extra?: Record<string, string>): Promise<void> {
|
|
74
|
+
await this.adb.subprocess.spawnAndWaitLegacy([
|
|
75
|
+
'am',
|
|
76
|
+
'broadcast',
|
|
77
|
+
'-a',
|
|
78
|
+
'com.android.systemui.demo',
|
|
79
|
+
'-e',
|
|
80
|
+
'command',
|
|
81
|
+
command,
|
|
82
|
+
...(extra ? Object.entries(extra).flatMap(([key, value]) => ['-e', key, value]) : []),
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public async setBatteryLevel(level: number): Promise<void> {
|
|
87
|
+
await this.broadcast('battery', { level: level.toString() });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async setBatteryCharging(value: boolean): Promise<void> {
|
|
91
|
+
await this.broadcast('battery', { plugged: value.toString() });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public async setPowerSaveMode(value: boolean): Promise<void> {
|
|
95
|
+
await this.broadcast('battery', { powersave: value.toString() });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async setAirplaneMode(show: boolean): Promise<void> {
|
|
99
|
+
await this.broadcast('network', { airplane: show ? 'show' : 'hide' });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public async setWifiSignalStrength(value: DemoModeSignalStrength): Promise<void> {
|
|
103
|
+
await this.broadcast('network', { wifi: 'show', level: value });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public async setMobileDataType(value: DemoModeMobileDataType): Promise<void> {
|
|
107
|
+
for (let i = 0; i < 2; i += 1) {
|
|
108
|
+
await this.broadcast('network', {
|
|
109
|
+
mobile: 'show',
|
|
110
|
+
sims: '1',
|
|
111
|
+
nosim: 'hide',
|
|
112
|
+
slot: '0',
|
|
113
|
+
datatype: value,
|
|
114
|
+
fully: 'true',
|
|
115
|
+
roam: 'false',
|
|
116
|
+
level: '4',
|
|
117
|
+
inflate: 'false',
|
|
118
|
+
activity: 'in',
|
|
119
|
+
carriernetworkchange: 'hide',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public async setMobileSignalStrength(value: DemoModeSignalStrength): Promise<void> {
|
|
125
|
+
await this.broadcast('network', { mobile: 'show', level: value });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async setNoSimCardIcon(show: boolean): Promise<void> {
|
|
129
|
+
await this.broadcast('network', { nosim: show ? 'show' : 'hide' });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public async setStatusBarMode(mode: DemoModeStatusBarMode): Promise<void> {
|
|
133
|
+
await this.broadcast('bars', { mode });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public async setVibrateModeEnabled(value: boolean): Promise<void> {
|
|
137
|
+
// 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('status', { volume: value ? 'vibrate' : 'hide' });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public async setBluetoothConnected(value: boolean): Promise<void> {
|
|
142
|
+
// 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('status', { bluetooth: value ? 'connected' : 'hide' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public async setLocatingIcon(show: boolean): Promise<void> {
|
|
147
|
+
await this.broadcast('status', { location: show ? 'show' : 'hide' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public async setAlarmIcon(show: boolean): Promise<void> {
|
|
151
|
+
await this.broadcast('status', { alarm: show ? 'show' : 'hide' });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public async setSyncingIcon(show: boolean): Promise<void> {
|
|
155
|
+
await this.broadcast('status', { sync: show ? 'show' : 'hide' });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public async setMuteIcon(show: boolean): Promise<void> {
|
|
159
|
+
await this.broadcast('status', { mute: show ? 'show' : 'hide' });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public async setSpeakerPhoneIcon(show: boolean): Promise<void> {
|
|
163
|
+
await this.broadcast('status', { speakerphone: show ? 'show' : 'hide' });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public async setNotificationsVisibility(show: boolean): Promise<void> {
|
|
167
|
+
// 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('notifications', { visible: show.toString() });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async setTime(hour: number, minute: number): Promise<void> {
|
|
172
|
+
await this.broadcast('clock', {
|
|
173
|
+
// cspell: disable-next-line
|
|
174
|
+
hhmm:
|
|
175
|
+
hour.toString().padStart(2, '0') +
|
|
176
|
+
minute.toString().padStart(2, '0'),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// cspell: ignore logcat
|
|
2
|
-
|
|
3
|
-
export * from './bug-report.js';
|
|
4
|
-
export * from './demo-mode.js';
|
|
5
|
-
export * from './logcat.js';
|
|
6
|
-
export * from './settings.js';
|
|
1
|
+
// cspell: ignore logcat
|
|
2
|
+
|
|
3
|
+
export * from './bug-report.js';
|
|
4
|
+
export * from './demo-mode.js';
|
|
5
|
+
export * from './logcat.js';
|
|
6
|
+
export * from './settings.js';
|
package/src/logcat.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// cspell: ignore logcat
|
|
2
2
|
|
|
3
|
-
import { AdbCommandBase,
|
|
4
|
-
import
|
|
3
|
+
import { AdbCommandBase, AdbSubprocessNoneProtocol } from '@yume-chan/adb';
|
|
4
|
+
import { BufferedTransformStream, DecodeUtf8Stream, SplitStringStream, WrapReadableStream, WritableStream, type ReadableStream } from '@yume-chan/stream-extra';
|
|
5
|
+
import Struct, { decodeUtf8, StructAsyncDeserializeStream } from '@yume-chan/struct';
|
|
5
6
|
|
|
6
7
|
// `adb logcat` is an alias to `adb shell logcat`
|
|
7
8
|
// so instead of adding to core library, it's implemented here
|
|
@@ -19,10 +20,12 @@ export enum LogId {
|
|
|
19
20
|
Kernel,
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=73;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
|
24
|
+
export enum AndroidLogPriority {
|
|
23
25
|
Unknown,
|
|
24
26
|
Default,
|
|
25
27
|
Verbose,
|
|
28
|
+
Debug,
|
|
26
29
|
Info,
|
|
27
30
|
Warn,
|
|
28
31
|
Error,
|
|
@@ -30,6 +33,41 @@ export enum LogPriority {
|
|
|
30
33
|
Silent,
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
// 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
|
+
[AndroidLogPriority.Unknown]: '?',
|
|
39
|
+
[AndroidLogPriority.Default]: '?',
|
|
40
|
+
[AndroidLogPriority.Verbose]: 'V',
|
|
41
|
+
[AndroidLogPriority.Debug]: 'D',
|
|
42
|
+
[AndroidLogPriority.Info]: 'I',
|
|
43
|
+
[AndroidLogPriority.Warn]: 'W',
|
|
44
|
+
[AndroidLogPriority.Error]: 'E',
|
|
45
|
+
[AndroidLogPriority.Fatal]: 'F',
|
|
46
|
+
[AndroidLogPriority.Silent]: 'S',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export enum LogcatFormat {
|
|
50
|
+
Brief,
|
|
51
|
+
Process,
|
|
52
|
+
Tag,
|
|
53
|
+
Thread,
|
|
54
|
+
Raw,
|
|
55
|
+
Time,
|
|
56
|
+
ThreadTime,
|
|
57
|
+
Long
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface LogcatFormatModifiers {
|
|
61
|
+
usec?: boolean;
|
|
62
|
+
printable?: boolean;
|
|
63
|
+
year?: boolean;
|
|
64
|
+
zone?: boolean;
|
|
65
|
+
epoch?: boolean;
|
|
66
|
+
monotonic?: boolean;
|
|
67
|
+
uid?: boolean;
|
|
68
|
+
descriptive?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
33
71
|
export interface LogcatOptions {
|
|
34
72
|
pid?: number;
|
|
35
73
|
ids?: LogId[];
|
|
@@ -56,17 +94,60 @@ export const LoggerEntry =
|
|
|
56
94
|
|
|
57
95
|
export type LoggerEntry = typeof LoggerEntry['TDeserializeResult'];
|
|
58
96
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
|
|
98
|
+
export interface AndroidLogEntry extends LoggerEntry {
|
|
99
|
+
priority: AndroidLogPriority;
|
|
100
|
+
tag: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
|
|
105
|
+
export function formatAndroidLogEntry(
|
|
106
|
+
entry: AndroidLogEntry,
|
|
107
|
+
format: LogcatFormat = LogcatFormat.Brief,
|
|
108
|
+
modifier?: LogcatFormatModifiers
|
|
109
|
+
) {
|
|
110
|
+
const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` : '';
|
|
111
|
+
|
|
112
|
+
switch (format) {
|
|
113
|
+
// TODO: implement other formats
|
|
114
|
+
default:
|
|
115
|
+
return `${AndroidLogPriorityToCharacter[entry.priority]}/${entry.tag.padEnd(8)}(${uid}${entry.pid.toString().padStart(5)}): ${entry.message}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function findTagEnd(payload: Uint8Array) {
|
|
120
|
+
for (const separator of [0, ' '.charCodeAt(0), ':'.charCodeAt(0)]) {
|
|
121
|
+
const index = payload.indexOf(separator);
|
|
122
|
+
if (index !== -1) {
|
|
123
|
+
return index;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const index = payload.findIndex(x => x >= 0x7f);
|
|
128
|
+
if (index !== -1) {
|
|
129
|
+
return index;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return payload.length;
|
|
62
133
|
}
|
|
63
134
|
|
|
64
|
-
export async function
|
|
65
|
-
const entry = await LoggerEntry.deserialize(stream);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
135
|
+
export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeStream): Promise<AndroidLogEntry> {
|
|
136
|
+
const entry = await LoggerEntry.deserialize(stream) as unknown as AndroidLogEntry;
|
|
137
|
+
if (entry.headerSize !== LoggerEntry.size) {
|
|
138
|
+
await stream.read(entry.headerSize - LoggerEntry.size);
|
|
139
|
+
}
|
|
140
|
+
let payload = await stream.read(entry.payloadSize);
|
|
141
|
+
|
|
142
|
+
// https://cs.android.com/android/platform/superproject/+/master:system/logging/logcat/logcat.cpp;l=193-194;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6
|
|
143
|
+
// TODO: payload for some log IDs are in binary format.
|
|
144
|
+
entry.priority = payload[0] as AndroidLogPriority;
|
|
145
|
+
|
|
146
|
+
payload = payload.subarray(1);
|
|
147
|
+
const tagEnd = findTagEnd(payload);
|
|
148
|
+
entry.tag = decodeUtf8(payload.subarray(0, tagEnd));
|
|
149
|
+
entry.message = tagEnd < payload.length - 1 ? decodeUtf8(payload.subarray(tagEnd + 1)) : '';
|
|
150
|
+
return entry;
|
|
70
151
|
}
|
|
71
152
|
|
|
72
153
|
export interface LogSize {
|
|
@@ -115,7 +196,7 @@ export class Logcat extends AdbCommandBase {
|
|
|
115
196
|
const result: LogSize[] = [];
|
|
116
197
|
await stdout
|
|
117
198
|
.pipeThrough(new DecodeUtf8Stream())
|
|
118
|
-
.pipeThrough(new
|
|
199
|
+
.pipeThrough(new SplitStringStream('\n'))
|
|
119
200
|
.pipeTo(new WritableStream({
|
|
120
201
|
write(chunk) {
|
|
121
202
|
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
|
@@ -154,34 +235,22 @@ export class Logcat extends AdbCommandBase {
|
|
|
154
235
|
]);
|
|
155
236
|
}
|
|
156
237
|
|
|
157
|
-
public binary(options?: LogcatOptions): ReadableStream<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (e instanceof BufferedStreamEndedError) {
|
|
175
|
-
controller.close();
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
throw e;
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
cancel() {
|
|
183
|
-
bufferedStream.close();
|
|
184
|
-
},
|
|
185
|
-
});
|
|
238
|
+
public binary(options?: LogcatOptions): ReadableStream<AndroidLogEntry> {
|
|
239
|
+
return new WrapReadableStream(async () => {
|
|
240
|
+
// TODO: make `spawn` return synchronously with streams pending
|
|
241
|
+
// so it's easier to chain them.
|
|
242
|
+
const { stdout } = await this.adb.subprocess.spawn([
|
|
243
|
+
'logcat',
|
|
244
|
+
'-B',
|
|
245
|
+
...(options?.pid ? ['--pid', options.pid.toString()] : []),
|
|
246
|
+
...(options?.ids ? ['-b', Logcat.joinLogId(options.ids)] : [])
|
|
247
|
+
], {
|
|
248
|
+
// PERF: None protocol is 150% faster then Shell protocol
|
|
249
|
+
protocols: [AdbSubprocessNoneProtocol],
|
|
250
|
+
});
|
|
251
|
+
return stdout;
|
|
252
|
+
}).pipeThrough(new BufferedTransformStream(stream => {
|
|
253
|
+
return deserializeAndroidLogEntry(stream);
|
|
254
|
+
}));
|
|
186
255
|
}
|
|
187
256
|
}
|