@yume-chan/adb 0.0.20 → 0.0.22
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 +45 -0
- package/CHANGELOG.md +22 -1
- package/README.md +46 -364
- package/esm/adb.d.ts +9 -5
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +21 -11
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts.map +1 -1
- package/esm/banner.js +1 -1
- package/esm/banner.js.map +1 -1
- package/esm/commands/base.d.ts.map +1 -1
- package/esm/commands/base.js.map +1 -1
- package/esm/commands/framebuffer.d.ts +9 -0
- package/esm/commands/framebuffer.d.ts.map +1 -1
- package/esm/commands/framebuffer.js +27 -3
- package/esm/commands/framebuffer.js.map +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/command.d.ts +1 -1
- package/esm/commands/subprocess/command.d.ts.map +1 -1
- package/esm/commands/subprocess/command.js +15 -13
- package/esm/commands/subprocess/command.js.map +1 -1
- package/esm/commands/subprocess/protocols/none.d.ts +2 -1
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +11 -14
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +21 -17
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/utils.js.map +1 -1
- package/esm/commands/sync/list.d.ts +2 -1
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -2
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +4 -0
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/push.js +1 -1
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +1 -1
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +3 -1
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +5 -3
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +1 -1
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +7 -3
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.js +2 -2
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +15 -0
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +17 -2
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +21 -0
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +18 -0
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/daemon/auth.d.ts +11 -9
- package/esm/daemon/auth.d.ts.map +1 -1
- package/esm/daemon/auth.js +21 -12
- package/esm/daemon/auth.js.map +1 -1
- package/esm/daemon/crypto.js +2 -2
- package/esm/daemon/crypto.js.map +1 -1
- package/esm/daemon/dispatcher.d.ts +6 -5
- package/esm/daemon/dispatcher.d.ts.map +1 -1
- package/esm/daemon/dispatcher.js +69 -58
- package/esm/daemon/dispatcher.js.map +1 -1
- package/esm/daemon/packet.d.ts +5 -5
- package/esm/daemon/packet.d.ts.map +1 -1
- package/esm/daemon/packet.js +2 -2
- package/esm/daemon/packet.js.map +1 -1
- package/esm/daemon/socket.d.ts +7 -14
- package/esm/daemon/socket.d.ts.map +1 -1
- package/esm/daemon/socket.js +58 -46
- package/esm/daemon/socket.js.map +1 -1
- package/esm/daemon/transport.d.ts +16 -4
- package/esm/daemon/transport.d.ts.map +1 -1
- package/esm/daemon/transport.js +11 -7
- package/esm/daemon/transport.js.map +1 -1
- package/esm/features.js +1 -1
- package/esm/features.js.map +1 -1
- package/esm/server/client.d.ts +12 -8
- package/esm/server/client.d.ts.map +1 -1
- package/esm/server/client.js +41 -22
- package/esm/server/client.js.map +1 -1
- package/esm/server/transport.d.ts.map +1 -1
- package/esm/server/transport.js.map +1 -1
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts.map +1 -1
- package/esm/utils/conditional-variable.js.map +1 -1
- package/esm/utils/hex.js.map +1 -1
- package/package.json +13 -14
- package/src/adb.ts +49 -35
- package/src/banner.ts +7 -7
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +31 -3
- package/src/commands/power.ts +9 -9
- package/src/commands/reverse.ts +13 -13
- package/src/commands/subprocess/command.ts +24 -25
- package/src/commands/subprocess/protocols/none.ts +23 -27
- package/src/commands/subprocess/protocols/shell.ts +42 -37
- package/src/commands/sync/list.ts +11 -7
- package/src/commands/sync/pull.ts +7 -4
- package/src/commands/sync/push.ts +6 -6
- package/src/commands/sync/request.ts +3 -3
- package/src/commands/sync/response.ts +8 -6
- package/src/commands/sync/socket.ts +19 -14
- package/src/commands/sync/stat.ts +5 -5
- package/src/commands/sync/sync.ts +32 -17
- package/src/commands/tcpip.ts +46 -2
- package/src/daemon/auth.ts +49 -30
- package/src/daemon/crypto.ts +9 -9
- package/src/daemon/dispatcher.ts +104 -88
- package/src/daemon/packet.ts +2 -2
- package/src/daemon/socket.ts +106 -115
- package/src/daemon/transport.ts +49 -29
- package/src/server/client.ts +88 -77
- package/src/server/transport.ts +13 -13
- package/src/utils/auto-reset-event.ts +4 -4
- package/src/utils/base64.ts +5 -5
- package/src/utils/conditional-variable.ts +4 -4
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/daemon/connection.d.ts +0 -11
- package/esm/daemon/connection.d.ts.map +0 -1
- package/esm/daemon/connection.js +0 -2
- package/esm/daemon/connection.js.map +0 -1
package/src/daemon/dispatcher.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AsyncOperationManager,
|
|
3
|
+
PromiseResolver,
|
|
4
|
+
delay,
|
|
5
|
+
} from "@yume-chan/async";
|
|
2
6
|
import type {
|
|
3
7
|
Consumable,
|
|
4
8
|
ReadableWritablePair,
|
|
@@ -12,7 +16,7 @@ import {
|
|
|
12
16
|
import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
|
|
13
17
|
|
|
14
18
|
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
|
15
|
-
import { decodeUtf8, encodeUtf8
|
|
19
|
+
import { decodeUtf8, encodeUtf8 } from "../utils/index.js";
|
|
16
20
|
|
|
17
21
|
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
|
18
22
|
import { AdbCommand, calculateChecksum } from "./packet.js";
|
|
@@ -28,6 +32,11 @@ export interface AdbPacketDispatcherOptions {
|
|
|
28
32
|
*/
|
|
29
33
|
appendNullToServiceString: boolean;
|
|
30
34
|
maxPayloadSize: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to preserve the connection open after the `AdbPacketDispatcher` is closed.
|
|
37
|
+
*/
|
|
38
|
+
preserveConnection?: boolean | undefined;
|
|
39
|
+
debugSlowRead?: boolean | undefined;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
/**
|
|
@@ -51,23 +60,23 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
51
60
|
|
|
52
61
|
#writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>;
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
readonly options: AdbPacketDispatcherOptions;
|
|
55
64
|
|
|
56
65
|
#closed = false;
|
|
57
66
|
#disconnected = new PromiseResolver<void>();
|
|
58
|
-
|
|
67
|
+
get disconnected() {
|
|
59
68
|
return this.#disconnected.promise;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
#incomingSocketHandlers = new Map<string, AdbIncomingSocketHandler>();
|
|
63
72
|
#readAbortController = new AbortController();
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
constructor(
|
|
66
75
|
connection: ReadableWritablePair<
|
|
67
76
|
AdbPacketData,
|
|
68
77
|
Consumable<AdbPacketInit>
|
|
69
78
|
>,
|
|
70
|
-
options: AdbPacketDispatcherOptions
|
|
79
|
+
options: AdbPacketDispatcherOptions,
|
|
71
80
|
) {
|
|
72
81
|
this.options = options;
|
|
73
82
|
|
|
@@ -76,29 +85,17 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
76
85
|
new WritableStream({
|
|
77
86
|
write: async (packet) => {
|
|
78
87
|
switch (packet.command) {
|
|
79
|
-
case AdbCommand.OK:
|
|
80
|
-
this.handleOk(packet);
|
|
81
|
-
break;
|
|
82
88
|
case AdbCommand.Close:
|
|
83
|
-
await this
|
|
89
|
+
await this.#handleClose(packet);
|
|
90
|
+
break;
|
|
91
|
+
case AdbCommand.Okay:
|
|
92
|
+
this.#handleOkay(packet);
|
|
84
93
|
break;
|
|
85
|
-
case AdbCommand.Write:
|
|
86
|
-
if (this.#sockets.has(packet.arg1)) {
|
|
87
|
-
await this.#sockets
|
|
88
|
-
.get(packet.arg1)!
|
|
89
|
-
.enqueue(packet.payload);
|
|
90
|
-
await this.sendPacket(
|
|
91
|
-
AdbCommand.OK,
|
|
92
|
-
packet.arg1,
|
|
93
|
-
packet.arg0
|
|
94
|
-
);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
throw new Error(
|
|
98
|
-
`Unknown local socket id: ${packet.arg1}`
|
|
99
|
-
);
|
|
100
94
|
case AdbCommand.Open:
|
|
101
|
-
await this
|
|
95
|
+
await this.#handleOpen(packet);
|
|
96
|
+
break;
|
|
97
|
+
case AdbCommand.Write:
|
|
98
|
+
await this.#handleWrite(packet);
|
|
102
99
|
break;
|
|
103
100
|
default:
|
|
104
101
|
// Junk data may only appear in the authentication phase,
|
|
@@ -107,62 +104,39 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
107
104
|
// (although it's possible that Adb added new commands in the future)
|
|
108
105
|
throw new Error(
|
|
109
106
|
`Unknown command: ${packet.command.toString(
|
|
110
|
-
16
|
|
111
|
-
)}
|
|
107
|
+
16,
|
|
108
|
+
)}`,
|
|
112
109
|
);
|
|
113
110
|
}
|
|
114
111
|
},
|
|
115
112
|
}),
|
|
116
113
|
{
|
|
117
|
-
|
|
118
|
-
// (device disconnection, protocol error, or user abortion)
|
|
119
|
-
// if the underlying streams are still open,
|
|
120
|
-
// it's still possible to create another ADB connection.
|
|
121
|
-
// So don't close `readable` here.
|
|
122
|
-
preventCancel: true,
|
|
114
|
+
preventCancel: options.preserveConnection ?? false,
|
|
123
115
|
signal: this.#readAbortController.signal,
|
|
124
|
-
}
|
|
116
|
+
},
|
|
125
117
|
)
|
|
126
118
|
.then(
|
|
127
119
|
() => {
|
|
128
|
-
this
|
|
120
|
+
this.#dispose();
|
|
129
121
|
},
|
|
130
122
|
(e) => {
|
|
131
123
|
if (!this.#closed) {
|
|
132
124
|
this.#disconnected.reject(e);
|
|
133
125
|
}
|
|
134
|
-
this
|
|
135
|
-
}
|
|
126
|
+
this.#dispose();
|
|
127
|
+
},
|
|
136
128
|
);
|
|
137
129
|
|
|
138
130
|
this.#writer = connection.writable.getWriter();
|
|
139
131
|
}
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
if (this.#initializers.resolve(packet.arg1, packet.arg0)) {
|
|
143
|
-
// Device successfully created the socket
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const socket = this.#sockets.get(packet.arg1);
|
|
148
|
-
if (socket) {
|
|
149
|
-
// Device has received last `WRTE` to the socket
|
|
150
|
-
socket.ack();
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Maybe the device is responding to a packet of last connection
|
|
155
|
-
// Tell the device to close the socket
|
|
156
|
-
void this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private async handleClose(packet: AdbPacketData) {
|
|
133
|
+
async #handleClose(packet: AdbPacketData) {
|
|
160
134
|
// If the socket is still pending
|
|
161
135
|
if (
|
|
162
136
|
packet.arg0 === 0 &&
|
|
163
137
|
this.#initializers.reject(
|
|
164
138
|
packet.arg1,
|
|
165
|
-
new Error("Socket open failed")
|
|
139
|
+
new Error("Socket open failed"),
|
|
166
140
|
)
|
|
167
141
|
) {
|
|
168
142
|
// Device failed to create the socket
|
|
@@ -183,15 +157,8 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
183
157
|
// Ignore `arg0` and search for the socket
|
|
184
158
|
const socket = this.#sockets.get(packet.arg1);
|
|
185
159
|
if (socket) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await this.sendPacket(
|
|
189
|
-
AdbCommand.Close,
|
|
190
|
-
packet.arg1,
|
|
191
|
-
packet.arg0
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
await socket.dispose();
|
|
160
|
+
await socket.close();
|
|
161
|
+
socket.dispose();
|
|
195
162
|
this.#sockets.delete(packet.arg1);
|
|
196
163
|
return;
|
|
197
164
|
}
|
|
@@ -201,22 +168,25 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
201
168
|
// the device may also respond with two `CLSE` packets.
|
|
202
169
|
}
|
|
203
170
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
171
|
+
#handleOkay(packet: AdbPacketData) {
|
|
172
|
+
if (this.#initializers.resolve(packet.arg1, packet.arg0)) {
|
|
173
|
+
// Device successfully created the socket
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
210
176
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
177
|
+
const socket = this.#sockets.get(packet.arg1);
|
|
178
|
+
if (socket) {
|
|
179
|
+
// Device has received last `WRTE` to the socket
|
|
180
|
+
socket.ack();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
214
183
|
|
|
215
|
-
|
|
216
|
-
|
|
184
|
+
// Maybe the device is responding to a packet of last connection
|
|
185
|
+
// Tell the device to close the socket
|
|
186
|
+
void this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0);
|
|
217
187
|
}
|
|
218
188
|
|
|
219
|
-
|
|
189
|
+
async #handleOpen(packet: AdbPacketData) {
|
|
220
190
|
// `AsyncOperationManager` doesn't support skipping IDs
|
|
221
191
|
// Use `add` + `resolve` to simulate this behavior
|
|
222
192
|
const [localId] = this.#initializers.add<number>();
|
|
@@ -245,13 +215,42 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
245
215
|
try {
|
|
246
216
|
await handler(controller.socket);
|
|
247
217
|
this.#sockets.set(localId, controller);
|
|
248
|
-
await this.sendPacket(AdbCommand.
|
|
218
|
+
await this.sendPacket(AdbCommand.Okay, localId, remoteId);
|
|
249
219
|
} catch (e) {
|
|
250
220
|
await this.sendPacket(AdbCommand.Close, 0, remoteId);
|
|
251
221
|
}
|
|
252
222
|
}
|
|
253
223
|
|
|
254
|
-
|
|
224
|
+
async #handleWrite(packet: AdbPacketData) {
|
|
225
|
+
const socket = this.#sockets.get(packet.arg1);
|
|
226
|
+
if (!socket) {
|
|
227
|
+
throw new Error(`Unknown local socket id: ${packet.arg1}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
let handled = false;
|
|
231
|
+
await Promise.race([
|
|
232
|
+
delay(5000).then(() => {
|
|
233
|
+
if (this.options.debugSlowRead && !handled) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`packet for \`${socket.service}\` not handled in 5 seconds`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
(async () => {
|
|
240
|
+
await socket.enqueue(packet.payload);
|
|
241
|
+
await this.sendPacket(
|
|
242
|
+
AdbCommand.Okay,
|
|
243
|
+
packet.arg1,
|
|
244
|
+
packet.arg0,
|
|
245
|
+
);
|
|
246
|
+
handled = true;
|
|
247
|
+
})(),
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async createSocket(service: string): Promise<AdbSocket> {
|
|
255
254
|
if (this.options.appendNullToServiceString) {
|
|
256
255
|
service += "\0";
|
|
257
256
|
}
|
|
@@ -273,11 +272,23 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
273
272
|
return controller.socket;
|
|
274
273
|
}
|
|
275
274
|
|
|
276
|
-
|
|
275
|
+
addReverseTunnel(service: string, handler: AdbIncomingSocketHandler) {
|
|
276
|
+
this.#incomingSocketHandlers.set(service, handler);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
removeReverseTunnel(address: string) {
|
|
280
|
+
this.#incomingSocketHandlers.delete(address);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
clearReverseTunnels() {
|
|
284
|
+
this.#incomingSocketHandlers.clear();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async sendPacket(
|
|
277
288
|
command: AdbCommand,
|
|
278
289
|
arg0: number,
|
|
279
290
|
arg1: number,
|
|
280
|
-
payload: string | Uint8Array = EMPTY_UINT8_ARRAY
|
|
291
|
+
payload: string | Uint8Array = EMPTY_UINT8_ARRAY,
|
|
281
292
|
): Promise<void> {
|
|
282
293
|
if (typeof payload === "string") {
|
|
283
294
|
payload = encodeUtf8(payload);
|
|
@@ -299,25 +310,30 @@ export class AdbPacketDispatcher implements Closeable {
|
|
|
299
310
|
});
|
|
300
311
|
}
|
|
301
312
|
|
|
302
|
-
|
|
313
|
+
async close() {
|
|
303
314
|
// Send `CLSE` packets for all sockets
|
|
304
315
|
await Promise.all(
|
|
305
|
-
Array.from(this.#sockets.values(), (socket) => socket.close())
|
|
316
|
+
Array.from(this.#sockets.values(), (socket) => socket.close()),
|
|
306
317
|
);
|
|
307
318
|
|
|
308
319
|
// Stop receiving
|
|
309
320
|
// It's possible that we haven't received all `CLSE` confirm packets,
|
|
310
321
|
// but it doesn't matter, the next connection can cope with them.
|
|
311
322
|
this.#closed = true;
|
|
323
|
+
|
|
312
324
|
this.#readAbortController.abort();
|
|
313
|
-
this
|
|
325
|
+
if (this.options.preserveConnection) {
|
|
326
|
+
this.#writer.releaseLock();
|
|
327
|
+
} else {
|
|
328
|
+
await this.#writer.close();
|
|
329
|
+
}
|
|
314
330
|
|
|
315
331
|
// `pipe().then()` will call `dispose`
|
|
316
332
|
}
|
|
317
333
|
|
|
318
|
-
|
|
334
|
+
#dispose() {
|
|
319
335
|
for (const socket of this.#sockets.values()) {
|
|
320
|
-
socket.dispose()
|
|
336
|
+
socket.dispose();
|
|
321
337
|
}
|
|
322
338
|
|
|
323
339
|
this.#disconnected.resolve();
|
package/src/daemon/packet.ts
CHANGED
|
@@ -5,7 +5,7 @@ export enum AdbCommand {
|
|
|
5
5
|
Auth = 0x48545541, // 'AUTH'
|
|
6
6
|
Close = 0x45534c43, // 'CLSE'
|
|
7
7
|
Connect = 0x4e584e43, // 'CNXN'
|
|
8
|
-
|
|
8
|
+
Okay = 0x59414b4f, // 'OKAY'
|
|
9
9
|
Open = 0x4e45504f, // 'OPEN'
|
|
10
10
|
Write = 0x45545257, // 'WRTE'
|
|
11
11
|
}
|
|
@@ -53,7 +53,7 @@ export class AdbPacketSerializeStream extends ConsumableTransformStream<
|
|
|
53
53
|
AdbPacketInit,
|
|
54
54
|
Uint8Array
|
|
55
55
|
> {
|
|
56
|
-
|
|
56
|
+
constructor() {
|
|
57
57
|
const headerBuffer = new Uint8Array(AdbPacketHeader.size);
|
|
58
58
|
super({
|
|
59
59
|
transform: async (chunk, controller) => {
|
package/src/daemon/socket.ts
CHANGED
|
@@ -4,18 +4,16 @@ import type {
|
|
|
4
4
|
Consumable,
|
|
5
5
|
PushReadableStreamController,
|
|
6
6
|
ReadableStream,
|
|
7
|
-
ReadableWritablePair,
|
|
8
7
|
WritableStream,
|
|
8
|
+
WritableStreamDefaultController,
|
|
9
9
|
} from "@yume-chan/stream-extra";
|
|
10
10
|
import {
|
|
11
11
|
ConsumableWritableStream,
|
|
12
|
-
DistributionStream,
|
|
13
|
-
DuplexStreamFactory,
|
|
14
12
|
PushReadableStream,
|
|
15
|
-
pipeFrom,
|
|
16
13
|
} from "@yume-chan/stream-extra";
|
|
17
14
|
|
|
18
|
-
import type {
|
|
15
|
+
import type { AdbSocket } from "../adb.js";
|
|
16
|
+
import { raceSignal } from "../server/index.js";
|
|
19
17
|
|
|
20
18
|
import type { AdbPacketDispatcher } from "./dispatcher.js";
|
|
21
19
|
import { AdbCommand } from "./packet.js";
|
|
@@ -36,130 +34,127 @@ export interface AdbDaemonSocketConstructionOptions
|
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
export class AdbDaemonSocketController
|
|
39
|
-
implements
|
|
40
|
-
AdbDaemonSocketInfo,
|
|
41
|
-
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>,
|
|
42
|
-
Closeable,
|
|
43
|
-
Disposable
|
|
37
|
+
implements AdbDaemonSocketInfo, AdbSocket, Disposable
|
|
44
38
|
{
|
|
45
|
-
|
|
39
|
+
readonly #dispatcher!: AdbPacketDispatcher;
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#duplex: DuplexStreamFactory<Uint8Array, Consumable<Uint8Array>>;
|
|
41
|
+
readonly localId!: number;
|
|
42
|
+
readonly remoteId!: number;
|
|
43
|
+
readonly localCreated!: boolean;
|
|
44
|
+
readonly service!: string;
|
|
53
45
|
|
|
54
46
|
#readable: ReadableStream<Uint8Array>;
|
|
55
47
|
#readableController!: PushReadableStreamController<Uint8Array>;
|
|
56
|
-
|
|
48
|
+
get readable() {
|
|
57
49
|
return this.#readable;
|
|
58
50
|
}
|
|
59
51
|
|
|
60
52
|
#writePromise: PromiseResolver<void> | undefined;
|
|
61
|
-
|
|
53
|
+
#writableController!: WritableStreamDefaultController;
|
|
54
|
+
readonly writable: WritableStream<Consumable<Uint8Array>>;
|
|
62
55
|
|
|
63
56
|
#closed = false;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.#duplex = new DuplexStreamFactory<
|
|
86
|
-
Uint8Array,
|
|
87
|
-
Consumable<Uint8Array>
|
|
88
|
-
>({
|
|
89
|
-
close: async () => {
|
|
90
|
-
this.#closed = true;
|
|
91
|
-
|
|
92
|
-
await this.dispatcher.sendPacket(
|
|
93
|
-
AdbCommand.Close,
|
|
94
|
-
this.localId,
|
|
95
|
-
this.remoteId
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// Don't `dispose` here, we need to wait for `CLSE` response packet.
|
|
99
|
-
return false;
|
|
100
|
-
},
|
|
101
|
-
dispose: () => {
|
|
102
|
-
// Error out the pending writes
|
|
103
|
-
this.#writePromise?.reject(new Error("Socket closed"));
|
|
104
|
-
},
|
|
57
|
+
|
|
58
|
+
#closedPromise = new PromiseResolver<void>();
|
|
59
|
+
get closed() {
|
|
60
|
+
return this.#closedPromise.promise;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#socket: AdbDaemonSocket;
|
|
64
|
+
get socket() {
|
|
65
|
+
return this.#socket;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
constructor(options: AdbDaemonSocketConstructionOptions) {
|
|
69
|
+
this.#dispatcher = options.dispatcher;
|
|
70
|
+
this.localId = options.localId;
|
|
71
|
+
this.remoteId = options.remoteId;
|
|
72
|
+
this.localCreated = options.localCreated;
|
|
73
|
+
this.service = options.service;
|
|
74
|
+
|
|
75
|
+
this.#readable = new PushReadableStream((controller) => {
|
|
76
|
+
this.#readableController = controller;
|
|
105
77
|
});
|
|
106
78
|
|
|
107
|
-
this
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
79
|
+
this.writable = new ConsumableWritableStream<Uint8Array>({
|
|
80
|
+
start: (controller) => {
|
|
81
|
+
this.#writableController = controller;
|
|
82
|
+
},
|
|
83
|
+
write: async (data, controller) => {
|
|
84
|
+
const size = data.length;
|
|
85
|
+
const chunkSize = this.#dispatcher.options.maxPayloadSize;
|
|
86
|
+
for (
|
|
87
|
+
let start = 0, end = chunkSize;
|
|
88
|
+
start < size;
|
|
89
|
+
start = end, end += chunkSize
|
|
90
|
+
) {
|
|
91
|
+
this.#writePromise = new PromiseResolver();
|
|
92
|
+
await this.#dispatcher.sendPacket(
|
|
93
|
+
AdbCommand.Write,
|
|
94
|
+
this.localId,
|
|
95
|
+
this.remoteId,
|
|
96
|
+
data.subarray(start, end),
|
|
97
|
+
);
|
|
98
|
+
// Wait for ack packet
|
|
99
|
+
await raceSignal(
|
|
100
|
+
() => this.#writePromise!.promise,
|
|
101
|
+
controller.signal,
|
|
102
|
+
);
|
|
117
103
|
}
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
this.writable = pipeFrom(
|
|
122
|
-
this.#duplex.createWritable(
|
|
123
|
-
new ConsumableWritableStream<Uint8Array>({
|
|
124
|
-
write: async (chunk) => {
|
|
125
|
-
// Wait for an ack packet
|
|
126
|
-
this.#writePromise = new PromiseResolver();
|
|
127
|
-
await this.dispatcher.sendPacket(
|
|
128
|
-
AdbCommand.Write,
|
|
129
|
-
this.localId,
|
|
130
|
-
this.remoteId,
|
|
131
|
-
chunk
|
|
132
|
-
);
|
|
133
|
-
await this.#writePromise.promise;
|
|
134
|
-
},
|
|
135
|
-
})
|
|
136
|
-
),
|
|
137
|
-
new DistributionStream(this.dispatcher.options.maxPayloadSize)
|
|
138
|
-
);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
139
106
|
|
|
140
|
-
this
|
|
107
|
+
this.#socket = new AdbDaemonSocket(this);
|
|
141
108
|
}
|
|
142
109
|
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
//
|
|
110
|
+
async enqueue(data: Uint8Array) {
|
|
111
|
+
// Consumers can `cancel` the `readable` if they are not interested in future data.
|
|
112
|
+
// Throw away the data if that happens.
|
|
146
113
|
if (this.#readableController.abortSignal.aborted) {
|
|
147
114
|
return;
|
|
148
115
|
}
|
|
149
116
|
|
|
150
|
-
|
|
117
|
+
try {
|
|
118
|
+
await this.#readableController.enqueue(data);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
if (this.#readableController.abortSignal.aborted) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
151
125
|
}
|
|
152
126
|
|
|
153
|
-
|
|
127
|
+
ack() {
|
|
154
128
|
this.#writePromise?.resolve();
|
|
155
129
|
}
|
|
156
130
|
|
|
157
|
-
|
|
158
|
-
|
|
131
|
+
async close(): Promise<void> {
|
|
132
|
+
if (this.#closed) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.#closed = true;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
this.#writableController.error(new Error("Socket closed"));
|
|
139
|
+
} catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await this.#dispatcher.sendPacket(
|
|
144
|
+
AdbCommand.Close,
|
|
145
|
+
this.localId,
|
|
146
|
+
this.remoteId,
|
|
147
|
+
);
|
|
159
148
|
}
|
|
160
149
|
|
|
161
|
-
|
|
162
|
-
|
|
150
|
+
dispose() {
|
|
151
|
+
try {
|
|
152
|
+
this.#readableController.close();
|
|
153
|
+
} catch {
|
|
154
|
+
// ignore
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.#closedPromise.resolve();
|
|
163
158
|
}
|
|
164
159
|
}
|
|
165
160
|
|
|
@@ -171,42 +166,38 @@ export class AdbDaemonSocketController
|
|
|
171
166
|
* `socket.writable.abort()`, `socket.writable.getWriter().abort()`,
|
|
172
167
|
* `socket.writable.close()` or `socket.writable.getWriter().close()`.
|
|
173
168
|
*/
|
|
174
|
-
export class AdbDaemonSocket
|
|
175
|
-
implements
|
|
176
|
-
AdbDaemonSocketInfo,
|
|
177
|
-
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
|
178
|
-
{
|
|
169
|
+
export class AdbDaemonSocket implements AdbDaemonSocketInfo, AdbSocket {
|
|
179
170
|
#controller: AdbDaemonSocketController;
|
|
180
171
|
|
|
181
|
-
|
|
172
|
+
get localId(): number {
|
|
182
173
|
return this.#controller.localId;
|
|
183
174
|
}
|
|
184
|
-
|
|
175
|
+
get remoteId(): number {
|
|
185
176
|
return this.#controller.remoteId;
|
|
186
177
|
}
|
|
187
|
-
|
|
178
|
+
get localCreated(): boolean {
|
|
188
179
|
return this.#controller.localCreated;
|
|
189
180
|
}
|
|
190
|
-
|
|
181
|
+
get service(): string {
|
|
191
182
|
return this.#controller.service;
|
|
192
183
|
}
|
|
193
184
|
|
|
194
|
-
|
|
185
|
+
get readable(): ReadableStream<Uint8Array> {
|
|
195
186
|
return this.#controller.readable;
|
|
196
187
|
}
|
|
197
|
-
|
|
188
|
+
get writable(): WritableStream<Consumable<Uint8Array>> {
|
|
198
189
|
return this.#controller.writable;
|
|
199
190
|
}
|
|
200
191
|
|
|
201
|
-
|
|
192
|
+
get closed(): Promise<void> {
|
|
202
193
|
return this.#controller.closed;
|
|
203
194
|
}
|
|
204
195
|
|
|
205
|
-
|
|
196
|
+
constructor(controller: AdbDaemonSocketController) {
|
|
206
197
|
this.#controller = controller;
|
|
207
198
|
}
|
|
208
199
|
|
|
209
|
-
|
|
200
|
+
close() {
|
|
210
201
|
return this.#controller.close();
|
|
211
202
|
}
|
|
212
203
|
}
|