@yume-chan/android-bin 0.0.17 → 0.0.19
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 +21 -0
- package/CHANGELOG.md +14 -1
- package/README.md +15 -0
- package/esm/bu.d.ts +14 -14
- package/esm/bu.d.ts.map +1 -1
- package/esm/bu.js +13 -9
- package/esm/bu.js.map +1 -1
- package/esm/bug-report.d.ts +37 -37
- package/esm/bug-report.d.ts.map +1 -1
- package/esm/bug-report.js +136 -124
- package/esm/bug-report.js.map +1 -1
- package/esm/cmd.d.ts +15 -0
- package/esm/cmd.d.ts.map +1 -0
- package/esm/cmd.js +54 -0
- package/esm/cmd.js.map +1 -0
- package/esm/demo-mode.d.ts +43 -42
- package/esm/demo-mode.d.ts.map +1 -1
- package/esm/demo-mode.js +179 -146
- package/esm/demo-mode.js.map +1 -1
- package/esm/index.d.ts +6 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +7 -5
- package/esm/index.js.map +1 -1
- package/esm/logcat.d.ts +91 -89
- package/esm/logcat.d.ts.map +1 -1
- package/esm/logcat.js +288 -192
- package/esm/logcat.js.map +1 -1
- package/esm/overlay-display.d.ts +21 -0
- package/esm/overlay-display.d.ts.map +1 -0
- package/esm/overlay-display.js +78 -0
- package/esm/overlay-display.js.map +1 -0
- package/esm/pm.d.ts +139 -0
- package/esm/pm.d.ts.map +1 -0
- package/esm/pm.js +125 -0
- package/esm/pm.js.map +1 -0
- package/esm/settings.d.ts +12 -12
- package/esm/settings.d.ts.map +1 -1
- package/esm/settings.js +29 -24
- package/esm/settings.js.map +1 -1
- package/package.json +15 -11
- package/src/bu.ts +7 -3
- package/src/bug-report.ts +74 -48
- package/src/cmd.ts +67 -0
- package/src/demo-mode.ts +111 -62
- package/src/index.ts +6 -4
- package/src/logcat.ts +359 -97
- package/src/overlay-display.ts +115 -0
- package/src/pm.ts +281 -0
- package/src/settings.ts +45 -16
- 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/logcat.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// cspell: ignore logcat
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
// cspell: ignore usec
|
|
3
|
+
|
|
4
|
+
import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
|
|
5
|
+
import type { ReadableStream } from "@yume-chan/stream-extra";
|
|
6
|
+
import {
|
|
7
|
+
BufferedTransformStream,
|
|
8
|
+
DecodeUtf8Stream,
|
|
9
|
+
SplitStringStream,
|
|
10
|
+
WrapReadableStream,
|
|
11
|
+
WritableStream,
|
|
12
|
+
} from "@yume-chan/stream-extra";
|
|
13
|
+
import type { StructAsyncDeserializeStream } from "@yume-chan/struct";
|
|
14
|
+
import Struct, { decodeUtf8 } from "@yume-chan/struct";
|
|
6
15
|
|
|
7
16
|
// `adb logcat` is an alias to `adb shell logcat`
|
|
8
17
|
// so instead of adding to core library, it's implemented here
|
|
@@ -34,17 +43,18 @@ export enum AndroidLogPriority {
|
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
// 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
|
-
|
|
46
|
+
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
|
47
|
+
{
|
|
48
|
+
[AndroidLogPriority.Unknown]: "?",
|
|
49
|
+
[AndroidLogPriority.Default]: "?",
|
|
50
|
+
[AndroidLogPriority.Verbose]: "V",
|
|
51
|
+
[AndroidLogPriority.Debug]: "D",
|
|
52
|
+
[AndroidLogPriority.Info]: "I",
|
|
53
|
+
[AndroidLogPriority.Warn]: "W",
|
|
54
|
+
[AndroidLogPriority.Error]: "E",
|
|
55
|
+
[AndroidLogPriority.Fatal]: "F",
|
|
56
|
+
[AndroidLogPriority.Silent]: "S",
|
|
57
|
+
};
|
|
48
58
|
|
|
49
59
|
export enum LogcatFormat {
|
|
50
60
|
Brief,
|
|
@@ -54,14 +64,15 @@ export enum LogcatFormat {
|
|
|
54
64
|
Raw,
|
|
55
65
|
Time,
|
|
56
66
|
ThreadTime,
|
|
57
|
-
Long
|
|
67
|
+
Long,
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
export interface LogcatFormatModifiers {
|
|
61
|
-
|
|
71
|
+
microseconds?: boolean;
|
|
72
|
+
nanoseconds?: boolean;
|
|
62
73
|
printable?: boolean;
|
|
63
74
|
year?: boolean;
|
|
64
|
-
|
|
75
|
+
timezone?: boolean;
|
|
65
76
|
epoch?: boolean;
|
|
66
77
|
monotonic?: boolean;
|
|
67
78
|
uid?: boolean;
|
|
@@ -76,55 +87,271 @@ 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("seconds")
|
|
96
|
+
.uint32("nanoseconds")
|
|
97
|
+
.uint32("logId")
|
|
98
|
+
.uint32("uid")
|
|
99
|
+
.extra({
|
|
100
|
+
get timestamp() {
|
|
101
|
+
return (
|
|
102
|
+
BigInt(this.seconds) * 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 {
|
|
99
112
|
priority: AndroidLogPriority;
|
|
100
113
|
tag: string;
|
|
101
114
|
message: string;
|
|
115
|
+
|
|
116
|
+
toString(format?: LogcatFormat, modifiers?: LogcatFormatModifiers): string;
|
|
102
117
|
}
|
|
103
118
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
function formatSeconds(seconds: number, modifiers: LogcatFormatModifiers) {
|
|
120
|
+
if (modifiers.monotonic) {
|
|
121
|
+
return seconds.toString().padStart(6);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (modifiers.epoch) {
|
|
125
|
+
return seconds.toString().padStart(19);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const date = new Date(seconds * 1000);
|
|
129
|
+
|
|
130
|
+
if (modifiers.year) {
|
|
131
|
+
// prettier-ignore
|
|
132
|
+
return `${
|
|
133
|
+
date.getFullYear().toString().padStart(4, "0")
|
|
134
|
+
}-${
|
|
135
|
+
(date.getMonth() + 1).toString().padStart(2, "0")
|
|
136
|
+
}-${
|
|
137
|
+
date.getDate().toString().padStart(2, "0")
|
|
138
|
+
} ${
|
|
139
|
+
date.getHours().toString().padStart(2, "0")
|
|
140
|
+
}:${
|
|
141
|
+
date.getMinutes().toString().padStart(2, "0")
|
|
142
|
+
}:${
|
|
143
|
+
date.getSeconds().toString().padStart(2, "0")
|
|
144
|
+
}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// prettier-ignore
|
|
148
|
+
return `${
|
|
149
|
+
(date.getMonth() + 1).toString().padStart(2, "0")
|
|
150
|
+
}-${
|
|
151
|
+
date.getDate().toString().padStart(2, "0")
|
|
152
|
+
} ${
|
|
153
|
+
date.getHours().toString().padStart(2, "0")
|
|
154
|
+
}:${
|
|
155
|
+
date.getMinutes().toString().padStart(2, "0")
|
|
156
|
+
}:${
|
|
157
|
+
date.getSeconds().toString().padStart(2, "0")
|
|
158
|
+
}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function formatNanoseconds(
|
|
162
|
+
nanoseconds: number,
|
|
163
|
+
modifiers: LogcatFormatModifiers
|
|
109
164
|
) {
|
|
110
|
-
|
|
165
|
+
if (modifiers.nanoseconds) {
|
|
166
|
+
return nanoseconds.toString().padStart(9, "0");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (modifiers.microseconds) {
|
|
170
|
+
return ((nanoseconds / 1000) | 0).toString().padStart(6, "0");
|
|
171
|
+
}
|
|
111
172
|
|
|
173
|
+
return ((nanoseconds / 1000000) | 0).toString().padStart(3, "0");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function formatTimezone(seconds: number, modifiers: LogcatFormatModifiers) {
|
|
177
|
+
if (!modifiers.timezone || modifiers.monotonic || modifiers.epoch) {
|
|
178
|
+
return "";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const date = new Date(seconds * 1000);
|
|
182
|
+
const offset = date.getTimezoneOffset();
|
|
183
|
+
const sign = offset <= 0 ? "+" : "-";
|
|
184
|
+
const absolute = Math.abs(offset);
|
|
185
|
+
const hours = (absolute / 60) | 0;
|
|
186
|
+
const minutes = absolute % 60;
|
|
187
|
+
|
|
188
|
+
// prettier-ignore
|
|
189
|
+
return ` ${
|
|
190
|
+
sign
|
|
191
|
+
}${
|
|
192
|
+
hours.toString().padStart(2, "0")
|
|
193
|
+
}:${
|
|
194
|
+
minutes.toString().padStart(2, "0")
|
|
195
|
+
}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function formatTime(
|
|
199
|
+
seconds: number,
|
|
200
|
+
nanoseconds: number,
|
|
201
|
+
modifiers: LogcatFormatModifiers
|
|
202
|
+
) {
|
|
203
|
+
const secondsString = formatSeconds(seconds, modifiers);
|
|
204
|
+
const nanosecondsString = formatNanoseconds(nanoseconds, modifiers);
|
|
205
|
+
const zoneString = formatTimezone(seconds, modifiers);
|
|
206
|
+
return `${secondsString}.${nanosecondsString}${zoneString}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function formatUid(
|
|
210
|
+
uid: number,
|
|
211
|
+
modifiers: LogcatFormatModifiers,
|
|
212
|
+
suffix: string
|
|
213
|
+
) {
|
|
214
|
+
return modifiers.uid ? `${uid.toString().padStart(5)}${suffix}` : "";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getFormatPrefix(
|
|
218
|
+
entry: AndroidLogEntry,
|
|
219
|
+
format: LogcatFormat,
|
|
220
|
+
modifiers: LogcatFormatModifiers
|
|
221
|
+
) {
|
|
222
|
+
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
|
|
112
223
|
switch (format) {
|
|
113
224
|
// TODO: implement other formats
|
|
225
|
+
case LogcatFormat.Tag:
|
|
226
|
+
// prettier-ignore
|
|
227
|
+
return `${
|
|
228
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
229
|
+
}/${
|
|
230
|
+
entry.tag.padEnd(8)
|
|
231
|
+
}: `;
|
|
232
|
+
case LogcatFormat.Process:
|
|
233
|
+
// prettier-ignore
|
|
234
|
+
return `${
|
|
235
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
236
|
+
}(${
|
|
237
|
+
formatUid(entry.uid, modifiers, ":")
|
|
238
|
+
}${
|
|
239
|
+
entry.pid.toString().padStart(5)
|
|
240
|
+
}) `;
|
|
241
|
+
case LogcatFormat.Thread:
|
|
242
|
+
// prettier-ignore
|
|
243
|
+
return `${
|
|
244
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
245
|
+
}(${
|
|
246
|
+
formatUid(entry.uid, modifiers, ":")
|
|
247
|
+
}${
|
|
248
|
+
entry.pid.toString().padStart(5)
|
|
249
|
+
}:${
|
|
250
|
+
entry.tid.toString().padStart(5)
|
|
251
|
+
}) `;
|
|
252
|
+
case LogcatFormat.Raw:
|
|
253
|
+
return "";
|
|
254
|
+
case LogcatFormat.Time:
|
|
255
|
+
// prettier-ignore
|
|
256
|
+
return `${
|
|
257
|
+
formatTime(entry.seconds, entry.nanoseconds, modifiers)
|
|
258
|
+
} ${
|
|
259
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
260
|
+
}/${
|
|
261
|
+
entry.tag.padEnd(8)
|
|
262
|
+
}(${
|
|
263
|
+
formatUid(entry.uid, modifiers, ":")
|
|
264
|
+
}${
|
|
265
|
+
entry.pid.toString().padStart(5)
|
|
266
|
+
}): `;
|
|
267
|
+
case LogcatFormat.ThreadTime:
|
|
268
|
+
// prettier-ignore
|
|
269
|
+
return `${
|
|
270
|
+
formatTime(entry.seconds, entry.nanoseconds, modifiers)
|
|
271
|
+
} ${
|
|
272
|
+
formatUid(entry.uid, modifiers, " ")
|
|
273
|
+
}${
|
|
274
|
+
entry.pid.toString().padStart(5)
|
|
275
|
+
} ${
|
|
276
|
+
entry.tid.toString().padStart(5)
|
|
277
|
+
} ${
|
|
278
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
279
|
+
} ${
|
|
280
|
+
entry.tag.toString().padEnd(8)
|
|
281
|
+
}: `;
|
|
282
|
+
case LogcatFormat.Brief:
|
|
283
|
+
default:
|
|
284
|
+
// prettier-ignore
|
|
285
|
+
return `${
|
|
286
|
+
AndroidLogPriorityToCharacter[entry.priority]
|
|
287
|
+
}/${
|
|
288
|
+
entry.tag.padEnd(8)
|
|
289
|
+
}(${
|
|
290
|
+
formatUid(entry.uid, modifiers, ":")
|
|
291
|
+
}${
|
|
292
|
+
entry.pid.toString().padStart(5)
|
|
293
|
+
}): `;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getFormatSuffix(entry: AndroidLogEntry, format: LogcatFormat) {
|
|
298
|
+
switch (format) {
|
|
299
|
+
case LogcatFormat.Process:
|
|
300
|
+
return ` (${entry.tag})`;
|
|
301
|
+
default:
|
|
302
|
+
return "";
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function formatEntryWrapLine(
|
|
307
|
+
entry: AndroidLogEntry,
|
|
308
|
+
format: LogcatFormat,
|
|
309
|
+
modifiers: LogcatFormatModifiers
|
|
310
|
+
) {
|
|
311
|
+
const prefix = getFormatPrefix(entry, format, modifiers);
|
|
312
|
+
const suffix = getFormatSuffix(entry, format);
|
|
313
|
+
return (
|
|
314
|
+
prefix + entry.message.replaceAll("\n", suffix + "\n" + prefix) + suffix
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function AndroidLogEntryToString(
|
|
319
|
+
this: AndroidLogEntry,
|
|
320
|
+
format: LogcatFormat = LogcatFormat.ThreadTime,
|
|
321
|
+
modifiers: LogcatFormatModifiers = {}
|
|
322
|
+
) {
|
|
323
|
+
switch (format) {
|
|
324
|
+
case LogcatFormat.Long:
|
|
325
|
+
// prettier-ignore
|
|
326
|
+
return `[ ${
|
|
327
|
+
formatTime(this.seconds, this.nanoseconds, modifiers)
|
|
328
|
+
} ${
|
|
329
|
+
formatUid(this.uid, modifiers, ":")
|
|
330
|
+
}${
|
|
331
|
+
this.pid.toString().padStart(5)
|
|
332
|
+
}:${
|
|
333
|
+
this.tid.toString().padStart(5)
|
|
334
|
+
} ${
|
|
335
|
+
AndroidLogPriorityToCharacter[this.priority]
|
|
336
|
+
}/${
|
|
337
|
+
this.tag.padEnd(8)
|
|
338
|
+
} ]\n${
|
|
339
|
+
this.message
|
|
340
|
+
}\n`;
|
|
114
341
|
default:
|
|
115
|
-
return
|
|
342
|
+
return formatEntryWrapLine(this, format, modifiers);
|
|
116
343
|
}
|
|
117
344
|
}
|
|
118
345
|
|
|
119
346
|
function findTagEnd(payload: Uint8Array) {
|
|
120
|
-
for (const separator of [0,
|
|
347
|
+
for (const separator of [0, " ".charCodeAt(0), ":".charCodeAt(0)]) {
|
|
121
348
|
const index = payload.indexOf(separator);
|
|
122
349
|
if (index !== -1) {
|
|
123
350
|
return index;
|
|
124
351
|
}
|
|
125
352
|
}
|
|
126
353
|
|
|
127
|
-
const index = payload.findIndex(x => x >= 0x7f);
|
|
354
|
+
const index = payload.findIndex((x) => x >= 0x7f);
|
|
128
355
|
if (index !== -1) {
|
|
129
356
|
return index;
|
|
130
357
|
}
|
|
@@ -132,8 +359,12 @@ function findTagEnd(payload: Uint8Array) {
|
|
|
132
359
|
return payload.length;
|
|
133
360
|
}
|
|
134
361
|
|
|
135
|
-
export async function deserializeAndroidLogEntry(
|
|
136
|
-
|
|
362
|
+
export async function deserializeAndroidLogEntry(
|
|
363
|
+
stream: StructAsyncDeserializeStream
|
|
364
|
+
): Promise<AndroidLogEntry> {
|
|
365
|
+
const entry = (await LoggerEntry.deserialize(
|
|
366
|
+
stream
|
|
367
|
+
)) as unknown as AndroidLogEntry;
|
|
137
368
|
if (entry.headerSize !== LoggerEntry.size) {
|
|
138
369
|
await stream.read(entry.headerSize - LoggerEntry.size);
|
|
139
370
|
}
|
|
@@ -146,7 +377,11 @@ export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeS
|
|
|
146
377
|
payload = payload.subarray(1);
|
|
147
378
|
const tagEnd = findTagEnd(payload);
|
|
148
379
|
entry.tag = decodeUtf8(payload.subarray(0, tagEnd));
|
|
149
|
-
entry.message =
|
|
380
|
+
entry.message =
|
|
381
|
+
tagEnd < payload.length - 1
|
|
382
|
+
? decodeUtf8(payload.subarray(tagEnd + 1))
|
|
383
|
+
: "";
|
|
384
|
+
entry.toString = AndroidLogEntryToString;
|
|
150
385
|
return entry;
|
|
151
386
|
}
|
|
152
387
|
|
|
@@ -166,72 +401,92 @@ export class Logcat extends AdbCommandBase {
|
|
|
166
401
|
|
|
167
402
|
public static logNameToId(name: string): LogId {
|
|
168
403
|
const key = name[0]!.toUpperCase() + name.substring(1);
|
|
169
|
-
return
|
|
404
|
+
return LogId[key as keyof typeof LogId];
|
|
170
405
|
}
|
|
171
406
|
|
|
172
407
|
public static joinLogId(ids: LogId[]): string {
|
|
173
|
-
return ids.map(id => Logcat.logIdToName(id)).join(
|
|
408
|
+
return ids.map((id) => Logcat.logIdToName(id)).join(",");
|
|
174
409
|
}
|
|
175
410
|
|
|
176
411
|
public static parseSize(value: number, multiplier: string): number {
|
|
177
|
-
const MULTIPLIERS = [
|
|
412
|
+
const MULTIPLIERS = ["", "Ki", "Mi", "Gi"];
|
|
178
413
|
return value * 1024 ** (MULTIPLIERS.indexOf(multiplier) || 0);
|
|
179
414
|
}
|
|
180
415
|
|
|
181
416
|
// TODO: logcat: Support output format before Android 10
|
|
182
417
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/748128
|
|
183
|
-
public static readonly LOG_SIZE_REGEX_10 =
|
|
418
|
+
public static readonly LOG_SIZE_REGEX_10 =
|
|
419
|
+
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
|
|
184
420
|
|
|
185
421
|
// Android 11 added `readable` part
|
|
186
422
|
// ref https://android-review.googlesource.com/c/platform/system/core/+/1390940
|
|
187
|
-
public static readonly LOG_SIZE_REGEX_11 =
|
|
423
|
+
public static readonly LOG_SIZE_REGEX_11 =
|
|
424
|
+
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
|
|
188
425
|
|
|
189
426
|
public async getLogSize(ids?: LogId[]): Promise<LogSize[]> {
|
|
190
427
|
const { stdout } = await this.adb.subprocess.spawn([
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
...(ids ? [
|
|
428
|
+
"logcat",
|
|
429
|
+
"-g",
|
|
430
|
+
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
|
194
431
|
]);
|
|
195
432
|
|
|
196
433
|
const result: LogSize[] = [];
|
|
197
434
|
await stdout
|
|
198
435
|
.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
|
-
|
|
436
|
+
.pipeThrough(new SplitStringStream("\n"))
|
|
437
|
+
.pipeTo(
|
|
438
|
+
new WritableStream({
|
|
439
|
+
write(chunk) {
|
|
440
|
+
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
|
441
|
+
if (match) {
|
|
442
|
+
result.push({
|
|
443
|
+
id: Logcat.logNameToId(match[1]!),
|
|
444
|
+
size: Logcat.parseSize(
|
|
445
|
+
Number.parseInt(match[2]!, 10),
|
|
446
|
+
match[3]!
|
|
447
|
+
),
|
|
448
|
+
readable: Logcat.parseSize(
|
|
449
|
+
Number.parseInt(match[6]!, 10),
|
|
450
|
+
match[7]!
|
|
451
|
+
),
|
|
452
|
+
consumed: Logcat.parseSize(
|
|
453
|
+
Number.parseInt(match[4]!, 10),
|
|
454
|
+
match[5]!
|
|
455
|
+
),
|
|
456
|
+
maxEntrySize: parseInt(match[8]!, 10),
|
|
457
|
+
maxPayloadSize: parseInt(match[9]!, 10),
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
|
|
463
|
+
if (match) {
|
|
464
|
+
result.push({
|
|
465
|
+
id: Logcat.logNameToId(match[1]!),
|
|
466
|
+
size: Logcat.parseSize(
|
|
467
|
+
Number.parseInt(match[2]!, 10),
|
|
468
|
+
match[3]!
|
|
469
|
+
),
|
|
470
|
+
consumed: Logcat.parseSize(
|
|
471
|
+
Number.parseInt(match[4]!, 10),
|
|
472
|
+
match[5]!
|
|
473
|
+
),
|
|
474
|
+
maxEntrySize: parseInt(match[6]!, 10),
|
|
475
|
+
maxPayloadSize: parseInt(match[7]!, 10),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
})
|
|
480
|
+
);
|
|
226
481
|
|
|
227
482
|
return result;
|
|
228
483
|
}
|
|
229
484
|
|
|
230
485
|
public async clear(ids?: LogId[]) {
|
|
231
486
|
await this.adb.subprocess.spawnAndWait([
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
...(ids ? [
|
|
487
|
+
"logcat",
|
|
488
|
+
"-c",
|
|
489
|
+
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
|
235
490
|
]);
|
|
236
491
|
}
|
|
237
492
|
|
|
@@ -239,18 +494,25 @@ export class Logcat extends AdbCommandBase {
|
|
|
239
494
|
return new WrapReadableStream(async () => {
|
|
240
495
|
// TODO: make `spawn` return synchronously with streams pending
|
|
241
496
|
// so it's easier to chain them.
|
|
242
|
-
const { stdout } = await this.adb.subprocess.spawn(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
497
|
+
const { stdout } = await this.adb.subprocess.spawn(
|
|
498
|
+
[
|
|
499
|
+
"logcat",
|
|
500
|
+
"-B",
|
|
501
|
+
...(options?.pid ? ["--pid", options.pid.toString()] : []),
|
|
502
|
+
...(options?.ids
|
|
503
|
+
? ["-b", Logcat.joinLogId(options.ids)]
|
|
504
|
+
: []),
|
|
505
|
+
],
|
|
506
|
+
{
|
|
507
|
+
// PERF: None protocol is 150% faster then Shell protocol
|
|
508
|
+
protocols: [AdbSubprocessNoneProtocol],
|
|
509
|
+
}
|
|
510
|
+
);
|
|
251
511
|
return stdout;
|
|
252
|
-
}).pipeThrough(
|
|
253
|
-
|
|
254
|
-
|
|
512
|
+
}).pipeThrough(
|
|
513
|
+
new BufferedTransformStream((stream) => {
|
|
514
|
+
return deserializeAndroidLogEntry(stream);
|
|
515
|
+
})
|
|
516
|
+
);
|
|
255
517
|
}
|
|
256
518
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Adb } from "@yume-chan/adb";
|
|
2
|
+
import { AdbCommandBase } from "@yume-chan/adb";
|
|
3
|
+
|
|
4
|
+
import { Settings } from "./settings.js";
|
|
5
|
+
|
|
6
|
+
export interface OverlayDisplayDeviceMode {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
density: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OverlayDisplayDevice {
|
|
13
|
+
modes: OverlayDisplayDeviceMode[];
|
|
14
|
+
secure: boolean;
|
|
15
|
+
ownContentOnly: boolean;
|
|
16
|
+
showSystemDecorations: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class OverlayDisplay extends AdbCommandBase {
|
|
20
|
+
private settings: Settings;
|
|
21
|
+
|
|
22
|
+
public static readonly OVERLAY_DISPLAY_DEVICES_KEY =
|
|
23
|
+
"overlay_display_devices";
|
|
24
|
+
|
|
25
|
+
constructor(adb: Adb) {
|
|
26
|
+
super(adb);
|
|
27
|
+
this.settings = new Settings(adb);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async get() {
|
|
31
|
+
const devices: OverlayDisplayDevice[] = [];
|
|
32
|
+
|
|
33
|
+
const settingString = await this.settings.get(
|
|
34
|
+
"global",
|
|
35
|
+
OverlayDisplay.OVERLAY_DISPLAY_DEVICES_KEY
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
for (const displayString of settingString.split(";")) {
|
|
39
|
+
const [modesString, ...flagStrings] = displayString.split(",");
|
|
40
|
+
|
|
41
|
+
if (!modesString) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const device: OverlayDisplayDevice = {
|
|
46
|
+
modes: [],
|
|
47
|
+
secure: false,
|
|
48
|
+
ownContentOnly: false,
|
|
49
|
+
showSystemDecorations: false,
|
|
50
|
+
};
|
|
51
|
+
for (const modeString of modesString.split("|")) {
|
|
52
|
+
const match = modeString.match(/(\d+)x(\d+)\/(\d+)/);
|
|
53
|
+
if (!match) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
device.modes.push({
|
|
57
|
+
width: parseInt(match[1]!, 10),
|
|
58
|
+
height: parseInt(match[2]!, 10),
|
|
59
|
+
density: parseInt(match[3]!, 10),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (device.modes.length === 0) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const flagString of flagStrings) {
|
|
67
|
+
switch (flagString) {
|
|
68
|
+
case "secure":
|
|
69
|
+
device.secure = true;
|
|
70
|
+
break;
|
|
71
|
+
case "own_content_only":
|
|
72
|
+
device.ownContentOnly = true;
|
|
73
|
+
break;
|
|
74
|
+
case "show_system_decorations":
|
|
75
|
+
device.showSystemDecorations = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
devices.push(device);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return devices;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async set(devices: OverlayDisplayDevice[]) {
|
|
86
|
+
let settingString = "";
|
|
87
|
+
for (const device of devices) {
|
|
88
|
+
if (settingString) {
|
|
89
|
+
settingString += ";";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
settingString += device.modes
|
|
93
|
+
.map((mode) => `${mode.width}x${mode.height}/${mode.density}`)
|
|
94
|
+
.join("|");
|
|
95
|
+
|
|
96
|
+
if (device.secure) {
|
|
97
|
+
settingString += ",secure";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (device.ownContentOnly) {
|
|
101
|
+
settingString += ",own_content_only";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (device.showSystemDecorations) {
|
|
105
|
+
settingString += ",show_system_decorations";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await this.settings.put(
|
|
110
|
+
"global",
|
|
111
|
+
OverlayDisplay.OVERLAY_DISPLAY_DEVICES_KEY,
|
|
112
|
+
settingString
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|