@yume-chan/adb 0.0.19 → 0.0.21
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 +57 -0
- package/CHANGELOG.md +26 -1
- package/README.md +222 -103
- package/esm/adb.d.ts +28 -45
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +34 -213
- package/esm/adb.js.map +1 -1
- package/esm/banner.d.ts +17 -0
- package/esm/banner.d.ts.map +1 -0
- package/esm/banner.js +67 -0
- package/esm/banner.js.map +1 -0
- 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 +1 -1
- package/esm/commands/power.d.ts.map +1 -1
- package/esm/commands/power.js +2 -2
- package/esm/commands/power.js.map +1 -1
- package/esm/commands/reverse.d.ts +17 -18
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +53 -48
- 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 +3 -8
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/none.js +16 -16
- package/esm/commands/subprocess/protocols/none.js.map +1 -1
- package/esm/commands/subprocess/protocols/shell.d.ts +3 -9
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
- package/esm/commands/subprocess/protocols/shell.js +63 -57
- package/esm/commands/subprocess/protocols/shell.js.map +1 -1
- package/esm/commands/subprocess/protocols/types.d.ts +1 -2
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
- package/esm/commands/sync/list.js +2 -2
- package/esm/commands/sync/push.d.ts +14 -3
- package/esm/commands/sync/push.d.ts.map +1 -1
- package/esm/commands/sync/push.js +9 -9
- package/esm/commands/sync/push.js.map +1 -1
- package/esm/commands/sync/request.js +2 -2
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +5 -3
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +7 -5
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/socket.d.ts +8 -14
- package/esm/commands/sync/socket.d.ts.map +1 -1
- package/esm/commands/sync/socket.js +35 -32
- package/esm/commands/sync/socket.js.map +1 -1
- package/esm/commands/sync/stat.js +4 -4
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +26 -18
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +36 -25
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/commands/tcpip.d.ts +2 -2
- package/esm/commands/tcpip.d.ts.map +1 -1
- package/esm/commands/tcpip.js +4 -2
- package/esm/commands/tcpip.js.map +1 -1
- package/esm/{auth.d.ts → daemon/auth.d.ts} +12 -11
- package/esm/daemon/auth.d.ts.map +1 -0
- package/esm/{auth.js → daemon/auth.js} +31 -22
- package/esm/daemon/auth.js.map +1 -0
- package/esm/{crypto.d.ts → daemon/crypto.d.ts} +5 -5
- package/esm/daemon/crypto.d.ts.map +1 -0
- package/esm/{crypto.js → daemon/crypto.js} +24 -28
- package/esm/daemon/crypto.js.map +1 -0
- package/esm/{backend.d.ts → daemon/device.d.ts} +2 -2
- package/esm/daemon/device.d.ts.map +1 -0
- package/esm/daemon/device.js +2 -0
- package/esm/daemon/device.js.map +1 -0
- package/esm/{socket → daemon}/dispatcher.d.ts +12 -30
- package/esm/daemon/dispatcher.d.ts.map +1 -0
- package/esm/{socket → daemon}/dispatcher.js +80 -78
- package/esm/daemon/dispatcher.js.map +1 -0
- package/esm/daemon/index.d.ts +8 -0
- package/esm/daemon/index.d.ts.map +1 -0
- package/esm/daemon/index.js +8 -0
- package/esm/daemon/index.js.map +1 -0
- package/esm/{packet.d.ts → daemon/packet.d.ts} +4 -4
- package/esm/daemon/packet.d.ts.map +1 -0
- package/esm/{packet.js → daemon/packet.js} +2 -2
- package/esm/{packet.js.map → daemon/packet.js.map} +1 -1
- package/esm/{socket → daemon}/socket.d.ts +15 -19
- package/esm/daemon/socket.d.ts.map +1 -0
- package/esm/{socket → daemon}/socket.js +49 -42
- package/esm/daemon/socket.js.map +1 -0
- package/esm/daemon/transport.d.ts +53 -0
- package/esm/daemon/transport.d.ts.map +1 -0
- package/esm/daemon/transport.js +186 -0
- package/esm/daemon/transport.js.map +1 -0
- package/esm/features.js +1 -1
- package/esm/features.js.map +1 -1
- package/esm/index.d.ts +3 -5
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -5
- package/esm/index.js.map +1 -1
- package/esm/server/client.d.ts +78 -0
- package/esm/server/client.d.ts.map +1 -0
- package/esm/server/client.js +328 -0
- package/esm/server/client.js.map +1 -0
- package/esm/server/index.d.ts +3 -0
- package/esm/server/index.d.ts.map +1 -0
- package/esm/server/index.js +3 -0
- package/esm/server/index.js.map +1 -0
- package/esm/server/transport.d.ts +19 -0
- package/esm/server/transport.d.ts.map +1 -0
- package/esm/server/transport.js +44 -0
- package/esm/server/transport.js.map +1 -0
- package/esm/utils/auto-reset-event.d.ts +1 -2
- package/esm/utils/auto-reset-event.d.ts.map +1 -1
- package/esm/utils/auto-reset-event.js +12 -12
- package/esm/utils/auto-reset-event.js.map +1 -1
- package/esm/utils/base64.js +5 -5
- package/esm/utils/base64.js.map +1 -1
- package/esm/utils/conditional-variable.d.ts +1 -2
- package/esm/utils/conditional-variable.d.ts.map +1 -1
- package/esm/utils/conditional-variable.js +10 -10
- package/esm/utils/conditional-variable.js.map +1 -1
- package/esm/utils/hex.d.ts +3 -0
- package/esm/utils/hex.d.ts.map +1 -0
- package/esm/utils/hex.js +55 -0
- package/esm/utils/hex.js.map +1 -0
- package/esm/utils/index.d.ts +2 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +2 -0
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/no-op.d.ts +7 -0
- package/esm/utils/no-op.d.ts.map +1 -0
- package/esm/utils/no-op.js +12 -0
- package/esm/utils/no-op.js.map +1 -0
- package/package.json +12 -12
- package/src/adb.ts +75 -274
- package/src/banner.ts +82 -0
- package/src/commands/base.ts +1 -1
- package/src/commands/framebuffer.ts +31 -3
- package/src/commands/power.ts +10 -10
- package/src/commands/reverse.ts +77 -82
- package/src/commands/subprocess/command.ts +24 -25
- package/src/commands/subprocess/protocols/none.ts +29 -30
- package/src/commands/subprocess/protocols/shell.ts +87 -89
- package/src/commands/subprocess/protocols/types.ts +1 -2
- package/src/commands/sync/list.ts +7 -7
- package/src/commands/sync/pull.ts +4 -4
- package/src/commands/sync/push.ts +28 -16
- package/src/commands/sync/request.ts +4 -4
- package/src/commands/sync/response.ts +13 -11
- package/src/commands/sync/socket.ts +48 -46
- package/src/commands/sync/stat.ts +5 -5
- package/src/commands/sync/sync.ts +55 -43
- package/src/commands/tcpip.ts +6 -4
- package/src/{auth.ts → daemon/auth.ts} +62 -43
- package/src/{crypto.ts → daemon/crypto.ts} +33 -41
- package/src/{backend.ts → daemon/device.ts} +1 -1
- package/src/{socket → daemon}/dispatcher.ts +103 -109
- package/src/daemon/index.ts +7 -0
- package/src/{packet.ts → daemon/packet.ts} +6 -6
- package/src/{socket → daemon}/socket.ts +77 -67
- package/src/daemon/transport.ts +281 -0
- package/src/index.ts +3 -5
- package/src/server/client.ts +473 -0
- package/src/server/index.ts +2 -0
- package/src/server/transport.ts +77 -0
- package/src/utils/auto-reset-event.ts +16 -16
- package/src/utils/base64.ts +5 -5
- package/src/utils/conditional-variable.ts +14 -14
- package/src/utils/hex.ts +58 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/no-op.ts +12 -0
- package/tsconfig.build.json +12 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/auth.d.ts.map +0 -1
- package/esm/auth.js.map +0 -1
- package/esm/backend.d.ts.map +0 -1
- package/esm/backend.js +0 -2
- package/esm/backend.js.map +0 -1
- package/esm/commands/install.d.ts +0 -10
- package/esm/commands/install.d.ts.map +0 -1
- package/esm/commands/install.js +0 -29
- package/esm/commands/install.js.map +0 -1
- package/esm/crypto.d.ts.map +0 -1
- package/esm/crypto.js.map +0 -1
- package/esm/packet.d.ts.map +0 -1
- package/esm/socket/dispatcher.d.ts.map +0 -1
- package/esm/socket/dispatcher.js.map +0 -1
- package/esm/socket/index.d.ts +0 -3
- package/esm/socket/index.d.ts.map +0 -1
- package/esm/socket/index.js +0 -3
- package/esm/socket/index.js.map +0 -1
- package/esm/socket/socket.d.ts.map +0 -1
- package/esm/socket/socket.js.map +0 -1
- package/src/socket/index.ts +0 -2
|
@@ -15,79 +15,85 @@ import {
|
|
|
15
15
|
pipeFrom,
|
|
16
16
|
} from "@yume-chan/stream-extra";
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import type { Closeable } from "../adb.js";
|
|
19
19
|
|
|
20
|
-
import type { AdbPacketDispatcher
|
|
20
|
+
import type { AdbPacketDispatcher } from "./dispatcher.js";
|
|
21
|
+
import { AdbCommand } from "./packet.js";
|
|
21
22
|
|
|
22
|
-
export interface
|
|
23
|
+
export interface AdbDaemonSocketInfo {
|
|
23
24
|
localId: number;
|
|
24
25
|
remoteId: number;
|
|
25
26
|
|
|
26
27
|
localCreated: boolean;
|
|
27
|
-
|
|
28
|
+
service: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export interface
|
|
31
|
+
export interface AdbDaemonSocketConstructionOptions
|
|
32
|
+
extends AdbDaemonSocketInfo {
|
|
31
33
|
dispatcher: AdbPacketDispatcher;
|
|
32
34
|
|
|
33
35
|
highWaterMark?: number | undefined;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
export class
|
|
38
|
+
export class AdbDaemonSocketController
|
|
37
39
|
implements
|
|
38
|
-
|
|
40
|
+
AdbDaemonSocketInfo,
|
|
39
41
|
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>,
|
|
40
42
|
Closeable,
|
|
41
43
|
Disposable
|
|
42
44
|
{
|
|
43
|
-
|
|
45
|
+
readonly #dispatcher!: AdbPacketDispatcher;
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
readonly localId!: number;
|
|
48
|
+
readonly remoteId!: number;
|
|
49
|
+
readonly localCreated!: boolean;
|
|
50
|
+
readonly service!: string;
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
#duplex: DuplexStreamFactory<Uint8Array, Consumable<Uint8Array>>;
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return this
|
|
54
|
+
#readable: ReadableStream<Uint8Array>;
|
|
55
|
+
#readableController!: PushReadableStreamController<Uint8Array>;
|
|
56
|
+
get readable() {
|
|
57
|
+
return this.#readable;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
#writePromise: PromiseResolver<void> | undefined;
|
|
61
|
+
readonly writable: WritableStream<Consumable<Uint8Array>>;
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
#closed = false;
|
|
62
64
|
/**
|
|
63
65
|
* Whether the socket is half-closed (i.e. the local side initiated the close).
|
|
64
66
|
*
|
|
65
67
|
* It's only used by dispatcher to avoid sending another `CLSE` packet to remote.
|
|
66
68
|
*/
|
|
67
|
-
|
|
68
|
-
return this
|
|
69
|
+
get closed() {
|
|
70
|
+
return this.#closed;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return this
|
|
73
|
+
#socket: AdbDaemonSocket;
|
|
74
|
+
get socket() {
|
|
75
|
+
return this.#socket;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
constructor(options: AdbDaemonSocketConstructionOptions) {
|
|
79
|
+
this.#dispatcher = options.dispatcher;
|
|
80
|
+
this.localId = options.localId;
|
|
81
|
+
this.remoteId = options.remoteId;
|
|
82
|
+
this.localCreated = options.localCreated;
|
|
83
|
+
this.service = options.service;
|
|
78
84
|
|
|
79
85
|
// Check this image to help you understand the stream graph
|
|
80
86
|
// cspell: disable-next-line
|
|
81
87
|
// https://www.plantuml.com/plantuml/png/TL0zoeGm4ErpYc3l5JxyS0yWM6mX5j4C6p4cxcJ25ejttuGX88ZftizxUKmJI275pGhXl0PP_UkfK_CAz5Z2hcWsW9Ny2fdU4C1f5aSchFVxA8vJjlTPRhqZzDQMRB7AklwJ0xXtX0ZSKH1h24ghoKAdGY23FhxC4nS2pDvxzIvxb-8THU0XlEQJ-ZB7SnXTAvc_LhOckhMdLBnbtndpb-SB7a8q2SRD_W00
|
|
82
88
|
|
|
83
|
-
this
|
|
89
|
+
this.#duplex = new DuplexStreamFactory<
|
|
84
90
|
Uint8Array,
|
|
85
91
|
Consumable<Uint8Array>
|
|
86
92
|
>({
|
|
87
93
|
close: async () => {
|
|
88
|
-
this
|
|
94
|
+
this.#closed = true;
|
|
89
95
|
|
|
90
|
-
await this
|
|
96
|
+
await this.#dispatcher.sendPacket(
|
|
91
97
|
AdbCommand.Close,
|
|
92
98
|
this.localId,
|
|
93
99
|
this.remoteId
|
|
@@ -98,14 +104,14 @@ export class AdbSocketController
|
|
|
98
104
|
},
|
|
99
105
|
dispose: () => {
|
|
100
106
|
// Error out the pending writes
|
|
101
|
-
this
|
|
107
|
+
this.#writePromise?.reject(new Error("Socket closed"));
|
|
102
108
|
},
|
|
103
109
|
});
|
|
104
110
|
|
|
105
|
-
this
|
|
111
|
+
this.#readable = this.#duplex.wrapReadable(
|
|
106
112
|
new PushReadableStream(
|
|
107
113
|
(controller) => {
|
|
108
|
-
this
|
|
114
|
+
this.#readableController = controller;
|
|
109
115
|
},
|
|
110
116
|
{
|
|
111
117
|
highWaterMark: options.highWaterMark ?? 16 * 1024,
|
|
@@ -117,47 +123,47 @@ export class AdbSocketController
|
|
|
117
123
|
);
|
|
118
124
|
|
|
119
125
|
this.writable = pipeFrom(
|
|
120
|
-
this.
|
|
126
|
+
this.#duplex.createWritable(
|
|
121
127
|
new ConsumableWritableStream<Uint8Array>({
|
|
122
128
|
write: async (chunk) => {
|
|
123
129
|
// Wait for an ack packet
|
|
124
|
-
this
|
|
125
|
-
await this
|
|
130
|
+
this.#writePromise = new PromiseResolver();
|
|
131
|
+
await this.#dispatcher.sendPacket(
|
|
126
132
|
AdbCommand.Write,
|
|
127
133
|
this.localId,
|
|
128
134
|
this.remoteId,
|
|
129
135
|
chunk
|
|
130
136
|
);
|
|
131
|
-
await this.
|
|
137
|
+
await this.#writePromise.promise;
|
|
132
138
|
},
|
|
133
139
|
})
|
|
134
140
|
),
|
|
135
|
-
new DistributionStream(this
|
|
141
|
+
new DistributionStream(this.#dispatcher.options.maxPayloadSize)
|
|
136
142
|
);
|
|
137
143
|
|
|
138
|
-
this
|
|
144
|
+
this.#socket = new AdbDaemonSocket(this);
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
async enqueue(data: Uint8Array) {
|
|
142
148
|
// Consumer may abort the `ReadableStream` to close the socket,
|
|
143
149
|
// it's OK to throw away further packets in this case.
|
|
144
|
-
if (this.
|
|
150
|
+
if (this.#readableController.abortSignal.aborted) {
|
|
145
151
|
return;
|
|
146
152
|
}
|
|
147
153
|
|
|
148
|
-
await this.
|
|
154
|
+
await this.#readableController.enqueue(data);
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
|
|
152
|
-
this
|
|
157
|
+
ack() {
|
|
158
|
+
this.#writePromise?.resolve();
|
|
153
159
|
}
|
|
154
160
|
|
|
155
|
-
|
|
156
|
-
await this.
|
|
161
|
+
async close(): Promise<void> {
|
|
162
|
+
await this.#duplex.close();
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
|
|
160
|
-
return this.
|
|
165
|
+
dispose() {
|
|
166
|
+
return this.#duplex.dispose();
|
|
161
167
|
}
|
|
162
168
|
}
|
|
163
169
|
|
|
@@ -169,38 +175,42 @@ export class AdbSocketController
|
|
|
169
175
|
* `socket.writable.abort()`, `socket.writable.getWriter().abort()`,
|
|
170
176
|
* `socket.writable.close()` or `socket.writable.getWriter().close()`.
|
|
171
177
|
*/
|
|
172
|
-
export class
|
|
178
|
+
export class AdbDaemonSocket
|
|
173
179
|
implements
|
|
174
|
-
|
|
180
|
+
AdbDaemonSocketInfo,
|
|
175
181
|
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
|
|
176
182
|
{
|
|
177
|
-
|
|
183
|
+
#controller: AdbDaemonSocketController;
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
return this.
|
|
185
|
+
get localId(): number {
|
|
186
|
+
return this.#controller.localId;
|
|
181
187
|
}
|
|
182
|
-
|
|
183
|
-
return this.
|
|
188
|
+
get remoteId(): number {
|
|
189
|
+
return this.#controller.remoteId;
|
|
184
190
|
}
|
|
185
|
-
|
|
186
|
-
return this.
|
|
191
|
+
get localCreated(): boolean {
|
|
192
|
+
return this.#controller.localCreated;
|
|
187
193
|
}
|
|
188
|
-
|
|
189
|
-
return this.
|
|
194
|
+
get service(): string {
|
|
195
|
+
return this.#controller.service;
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
|
|
193
|
-
return this.
|
|
198
|
+
get readable(): ReadableStream<Uint8Array> {
|
|
199
|
+
return this.#controller.readable;
|
|
194
200
|
}
|
|
195
|
-
|
|
196
|
-
return this.
|
|
201
|
+
get writable(): WritableStream<Consumable<Uint8Array>> {
|
|
202
|
+
return this.#controller.writable;
|
|
197
203
|
}
|
|
198
204
|
|
|
199
|
-
|
|
200
|
-
this.
|
|
205
|
+
get closed(): boolean {
|
|
206
|
+
return this.#controller.closed;
|
|
201
207
|
}
|
|
202
208
|
|
|
203
|
-
|
|
204
|
-
|
|
209
|
+
constructor(controller: AdbDaemonSocketController) {
|
|
210
|
+
this.#controller = controller;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
close() {
|
|
214
|
+
return this.#controller.close();
|
|
205
215
|
}
|
|
206
216
|
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import type { Consumable, ReadableWritablePair } from "@yume-chan/stream-extra";
|
|
3
|
+
import {
|
|
4
|
+
AbortController,
|
|
5
|
+
ConsumableWritableStream,
|
|
6
|
+
WritableStream,
|
|
7
|
+
} from "@yume-chan/stream-extra";
|
|
8
|
+
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
9
|
+
import { decodeUtf8, encodeUtf8 } from "@yume-chan/struct";
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
AdbIncomingSocketHandler,
|
|
13
|
+
AdbSocket,
|
|
14
|
+
AdbTransport,
|
|
15
|
+
} from "../adb.js";
|
|
16
|
+
import { AdbBanner } from "../banner.js";
|
|
17
|
+
import { AdbFeature } from "../features.js";
|
|
18
|
+
|
|
19
|
+
import type { AdbAuthenticator, AdbCredentialStore } from "./auth.js";
|
|
20
|
+
import {
|
|
21
|
+
ADB_DEFAULT_AUTHENTICATORS,
|
|
22
|
+
AdbAuthenticationProcessor,
|
|
23
|
+
} from "./auth.js";
|
|
24
|
+
import { AdbPacketDispatcher } from "./dispatcher.js";
|
|
25
|
+
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
|
26
|
+
import { AdbCommand, calculateChecksum } from "./packet.js";
|
|
27
|
+
|
|
28
|
+
export const ADB_DAEMON_VERSION_OMIT_CHECKSUM = 0x01000001;
|
|
29
|
+
|
|
30
|
+
interface AdbDaemonAuthenticationOptions {
|
|
31
|
+
serial: string;
|
|
32
|
+
connection: ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>;
|
|
33
|
+
credentialStore: AdbCredentialStore;
|
|
34
|
+
authenticators?: AdbAuthenticator[];
|
|
35
|
+
/**
|
|
36
|
+
* Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
|
|
37
|
+
*/
|
|
38
|
+
preserveConnection?: boolean | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface AdbDaemonSocketConnectorConstructionOptions {
|
|
42
|
+
serial: string;
|
|
43
|
+
connection: ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>;
|
|
44
|
+
version: number;
|
|
45
|
+
maxPayloadSize: number;
|
|
46
|
+
banner: string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
|
|
49
|
+
*/
|
|
50
|
+
preserveConnection?: boolean | undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class AdbDaemonTransport implements AdbTransport {
|
|
54
|
+
/**
|
|
55
|
+
* Authenticates the connection and creates an `AdbDaemonTransport` instance
|
|
56
|
+
* that can be used by `Adb` class.
|
|
57
|
+
*
|
|
58
|
+
* If an authentication process failed, it's possible to call `authenticate` again
|
|
59
|
+
* on the same connection. Because every time the device receives a `CNXN` packet,
|
|
60
|
+
* it resets all internal state, and starts a new authentication process.
|
|
61
|
+
*/
|
|
62
|
+
static async authenticate({
|
|
63
|
+
serial,
|
|
64
|
+
connection,
|
|
65
|
+
credentialStore,
|
|
66
|
+
authenticators = ADB_DEFAULT_AUTHENTICATORS,
|
|
67
|
+
preserveConnection,
|
|
68
|
+
}: AdbDaemonAuthenticationOptions): Promise<AdbDaemonTransport> {
|
|
69
|
+
// Initially, set to highest-supported version and payload size.
|
|
70
|
+
let version = 0x01000001;
|
|
71
|
+
// Android 4: 4K, Android 7: 256K, Android 9: 1M
|
|
72
|
+
let maxPayloadSize = 1024 * 1024;
|
|
73
|
+
|
|
74
|
+
const resolver = new PromiseResolver<string>();
|
|
75
|
+
const authProcessor = new AdbAuthenticationProcessor(
|
|
76
|
+
authenticators,
|
|
77
|
+
credentialStore,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Here is similar to `AdbPacketDispatcher`,
|
|
81
|
+
// But the received packet types and send packet processing are different.
|
|
82
|
+
const abortController = new AbortController();
|
|
83
|
+
const pipe = connection.readable
|
|
84
|
+
.pipeTo(
|
|
85
|
+
new WritableStream({
|
|
86
|
+
async write(packet) {
|
|
87
|
+
switch (packet.command) {
|
|
88
|
+
case AdbCommand.Connect:
|
|
89
|
+
version = Math.min(version, packet.arg0);
|
|
90
|
+
maxPayloadSize = Math.min(
|
|
91
|
+
maxPayloadSize,
|
|
92
|
+
packet.arg1,
|
|
93
|
+
);
|
|
94
|
+
resolver.resolve(decodeUtf8(packet.payload));
|
|
95
|
+
break;
|
|
96
|
+
case AdbCommand.Auth: {
|
|
97
|
+
const response = await authProcessor.process(
|
|
98
|
+
packet,
|
|
99
|
+
);
|
|
100
|
+
await sendPacket(response);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
default:
|
|
104
|
+
// Maybe the previous ADB client exited without reading all packets,
|
|
105
|
+
// so they are still waiting in OS internal buffer.
|
|
106
|
+
// Just ignore them.
|
|
107
|
+
// Because a `Connect` packet will reset the device,
|
|
108
|
+
// Eventually there will be `Connect` and `Auth` response packets.
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
{
|
|
114
|
+
// Don't cancel the source ReadableStream on AbortSignal abort.
|
|
115
|
+
preventCancel: true,
|
|
116
|
+
signal: abortController.signal,
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
.then(
|
|
120
|
+
() => {
|
|
121
|
+
if (resolver.state === "running") {
|
|
122
|
+
resolver.reject(
|
|
123
|
+
new Error("Connection closed unexpectedly"),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
(e) => {
|
|
128
|
+
resolver.reject(e);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const writer = connection.writable.getWriter();
|
|
133
|
+
async function sendPacket(init: AdbPacketData) {
|
|
134
|
+
// Always send checksum in auth steps
|
|
135
|
+
// Because we don't know if the device needs it or not.
|
|
136
|
+
(init as AdbPacketInit).checksum = calculateChecksum(init.payload);
|
|
137
|
+
(init as AdbPacketInit).magic = init.command ^ 0xffffffff;
|
|
138
|
+
await ConsumableWritableStream.write(writer, init as AdbPacketInit);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let banner: string;
|
|
142
|
+
try {
|
|
143
|
+
// https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
|
|
144
|
+
// There are some other feature constants, but some of them are only used by ADB server, not devices (daemons).
|
|
145
|
+
const features = [
|
|
146
|
+
AdbFeature.ShellV2,
|
|
147
|
+
AdbFeature.Cmd,
|
|
148
|
+
AdbFeature.StatV2,
|
|
149
|
+
AdbFeature.ListV2,
|
|
150
|
+
AdbFeature.FixedPushMkdir,
|
|
151
|
+
"apex",
|
|
152
|
+
AdbFeature.Abb,
|
|
153
|
+
// only tells the client the symlink timestamp issue in `adb push --sync` has been fixed.
|
|
154
|
+
// No special handling required.
|
|
155
|
+
"fixed_push_symlink_timestamp",
|
|
156
|
+
AdbFeature.AbbExec,
|
|
157
|
+
"remount_shell",
|
|
158
|
+
"track_app",
|
|
159
|
+
AdbFeature.SendReceiveV2,
|
|
160
|
+
"sendrecv_v2_brotli",
|
|
161
|
+
"sendrecv_v2_lz4",
|
|
162
|
+
"sendrecv_v2_zstd",
|
|
163
|
+
"sendrecv_v2_dry_run_send",
|
|
164
|
+
].join(",");
|
|
165
|
+
|
|
166
|
+
await sendPacket({
|
|
167
|
+
command: AdbCommand.Connect,
|
|
168
|
+
arg0: version,
|
|
169
|
+
arg1: maxPayloadSize,
|
|
170
|
+
// The terminating `;` is required in formal definition
|
|
171
|
+
// But ADB daemon (all versions) can still work without it
|
|
172
|
+
payload: encodeUtf8(`host::features=${features}`),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
banner = await resolver.promise;
|
|
176
|
+
} finally {
|
|
177
|
+
// When failed, release locks on `connection` so the caller can try again.
|
|
178
|
+
// When success, also release locks so `AdbPacketDispatcher` can use them.
|
|
179
|
+
abortController.abort();
|
|
180
|
+
writer.releaseLock();
|
|
181
|
+
|
|
182
|
+
// Wait until pipe stops (`ReadableStream` lock released)
|
|
183
|
+
await pipe;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return new AdbDaemonTransport({
|
|
187
|
+
serial,
|
|
188
|
+
connection,
|
|
189
|
+
version,
|
|
190
|
+
maxPayloadSize,
|
|
191
|
+
banner,
|
|
192
|
+
preserveConnection,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
readonly #dispatcher: AdbPacketDispatcher;
|
|
197
|
+
|
|
198
|
+
#serial: string;
|
|
199
|
+
get serial() {
|
|
200
|
+
return this.#serial;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#protocolVersion: number;
|
|
204
|
+
get protocolVersion() {
|
|
205
|
+
return this.#protocolVersion;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#maxPayloadSize: number;
|
|
209
|
+
get maxPayloadSize() {
|
|
210
|
+
return this.#maxPayloadSize;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#banner: AdbBanner;
|
|
214
|
+
get banner() {
|
|
215
|
+
return this.#banner;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
get disconnected() {
|
|
219
|
+
return this.#dispatcher.disconnected;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
constructor({
|
|
223
|
+
serial,
|
|
224
|
+
connection,
|
|
225
|
+
version,
|
|
226
|
+
maxPayloadSize,
|
|
227
|
+
banner,
|
|
228
|
+
preserveConnection,
|
|
229
|
+
}: AdbDaemonSocketConnectorConstructionOptions) {
|
|
230
|
+
this.#serial = serial;
|
|
231
|
+
this.#banner = AdbBanner.parse(banner);
|
|
232
|
+
|
|
233
|
+
let calculateChecksum: boolean;
|
|
234
|
+
let appendNullToServiceString: boolean;
|
|
235
|
+
if (version >= ADB_DAEMON_VERSION_OMIT_CHECKSUM) {
|
|
236
|
+
calculateChecksum = false;
|
|
237
|
+
appendNullToServiceString = false;
|
|
238
|
+
} else {
|
|
239
|
+
calculateChecksum = true;
|
|
240
|
+
appendNullToServiceString = true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
this.#dispatcher = new AdbPacketDispatcher(connection, {
|
|
244
|
+
calculateChecksum,
|
|
245
|
+
appendNullToServiceString,
|
|
246
|
+
maxPayloadSize,
|
|
247
|
+
preserveConnection,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
this.#protocolVersion = version;
|
|
251
|
+
this.#maxPayloadSize = maxPayloadSize;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
connect(service: string): ValueOrPromise<AdbSocket> {
|
|
255
|
+
return this.#dispatcher.createSocket(service);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
addReverseTunnel(
|
|
259
|
+
handler: AdbIncomingSocketHandler,
|
|
260
|
+
address?: string,
|
|
261
|
+
): string {
|
|
262
|
+
if (!address) {
|
|
263
|
+
const id = Math.random().toString().substring(2);
|
|
264
|
+
address = `localabstract:reverse_${id}`;
|
|
265
|
+
}
|
|
266
|
+
this.#dispatcher.addReverseTunnel(address, handler);
|
|
267
|
+
return address;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
removeReverseTunnel(address: string): void {
|
|
271
|
+
this.#dispatcher.removeReverseTunnel(address);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
clearReverseTunnels(): void {
|
|
275
|
+
this.#dispatcher.clearReverseTunnels();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
close(): ValueOrPromise<void> {
|
|
279
|
+
return this.#dispatcher.close();
|
|
280
|
+
}
|
|
281
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export * from "./adb.js";
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./backend.js";
|
|
2
|
+
export * from "./banner.js";
|
|
4
3
|
export * from "./commands/index.js";
|
|
5
|
-
export * from "./
|
|
4
|
+
export * from "./daemon/index.js";
|
|
6
5
|
export * from "./features.js";
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./socket/index.js";
|
|
6
|
+
export * from "./server/index.js";
|
|
9
7
|
export * from "./utils/index.js";
|