@yume-chan/adb 0.0.22 → 0.0.24

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.
Files changed (149) hide show
  1. package/CHANGELOG.json +51 -0
  2. package/CHANGELOG.md +24 -1
  3. package/LICENSE +1 -1
  4. package/esm/adb.d.ts +6 -3
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +11 -4
  7. package/esm/adb.js.map +1 -1
  8. package/esm/banner.d.ts.map +1 -1
  9. package/esm/banner.js +1 -0
  10. package/esm/banner.js.map +1 -1
  11. package/esm/commands/power.d.ts.map +1 -1
  12. package/esm/commands/power.js +6 -5
  13. package/esm/commands/power.js.map +1 -1
  14. package/esm/commands/reverse.d.ts.map +1 -1
  15. package/esm/commands/reverse.js +23 -11
  16. package/esm/commands/reverse.js.map +1 -1
  17. package/esm/commands/subprocess/command.js +3 -3
  18. package/esm/commands/subprocess/command.js.map +1 -1
  19. package/esm/commands/subprocess/protocols/none.d.ts +2 -2
  20. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  21. package/esm/commands/subprocess/protocols/shell.d.ts +3 -3
  22. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  23. package/esm/commands/subprocess/protocols/shell.js +14 -69
  24. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  25. package/esm/commands/subprocess/protocols/types.d.ts +2 -2
  26. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
  27. package/esm/commands/subprocess/utils.js +1 -1
  28. package/esm/commands/subprocess/utils.js.map +1 -1
  29. package/esm/commands/sync/list.d.ts +2 -5
  30. package/esm/commands/sync/list.d.ts.map +1 -1
  31. package/esm/commands/sync/list.js +2 -4
  32. package/esm/commands/sync/list.js.map +1 -1
  33. package/esm/commands/sync/pull.d.ts +1 -4
  34. package/esm/commands/sync/pull.d.ts.map +1 -1
  35. package/esm/commands/sync/pull.js +2 -2
  36. package/esm/commands/sync/pull.js.map +1 -1
  37. package/esm/commands/sync/push.d.ts +4 -7
  38. package/esm/commands/sync/push.d.ts.map +1 -1
  39. package/esm/commands/sync/push.js +3 -6
  40. package/esm/commands/sync/push.js.map +1 -1
  41. package/esm/commands/sync/request.d.ts +13 -18
  42. package/esm/commands/sync/request.d.ts.map +1 -1
  43. package/esm/commands/sync/request.js +23 -28
  44. package/esm/commands/sync/request.js.map +1 -1
  45. package/esm/commands/sync/response.d.ts +18 -12
  46. package/esm/commands/sync/response.d.ts.map +1 -1
  47. package/esm/commands/sync/response.js +39 -17
  48. package/esm/commands/sync/response.js.map +1 -1
  49. package/esm/commands/sync/socket.d.ts +2 -2
  50. package/esm/commands/sync/socket.d.ts.map +1 -1
  51. package/esm/commands/sync/socket.js +10 -5
  52. package/esm/commands/sync/socket.js.map +1 -1
  53. package/esm/commands/sync/stat.d.ts +0 -3
  54. package/esm/commands/sync/stat.d.ts.map +1 -1
  55. package/esm/commands/sync/stat.js +0 -2
  56. package/esm/commands/sync/stat.js.map +1 -1
  57. package/esm/commands/sync/sync.d.ts +4 -3
  58. package/esm/commands/sync/sync.d.ts.map +1 -1
  59. package/esm/commands/sync/sync.js +9 -7
  60. package/esm/commands/sync/sync.js.map +1 -1
  61. package/esm/commands/tcpip.js +1 -1
  62. package/esm/commands/tcpip.js.map +1 -1
  63. package/esm/daemon/auth.js +1 -1
  64. package/esm/daemon/auth.js.map +1 -1
  65. package/esm/daemon/crypto.d.ts +3 -2
  66. package/esm/daemon/crypto.d.ts.map +1 -1
  67. package/esm/daemon/crypto.js +56 -49
  68. package/esm/daemon/crypto.js.map +1 -1
  69. package/esm/daemon/dispatcher.d.ts +38 -6
  70. package/esm/daemon/dispatcher.d.ts.map +1 -1
  71. package/esm/daemon/dispatcher.js +86 -28
  72. package/esm/daemon/dispatcher.js.map +1 -1
  73. package/esm/daemon/packet.d.ts +2 -2
  74. package/esm/daemon/packet.d.ts.map +1 -1
  75. package/esm/daemon/packet.js +13 -12
  76. package/esm/daemon/packet.js.map +1 -1
  77. package/esm/daemon/socket.d.ts +11 -6
  78. package/esm/daemon/socket.d.ts.map +1 -1
  79. package/esm/daemon/socket.js +42 -12
  80. package/esm/daemon/socket.js.map +1 -1
  81. package/esm/daemon/transport.d.ts +75 -11
  82. package/esm/daemon/transport.d.ts.map +1 -1
  83. package/esm/daemon/transport.js +60 -33
  84. package/esm/daemon/transport.js.map +1 -1
  85. package/esm/features.d.ts +2 -1
  86. package/esm/features.d.ts.map +1 -1
  87. package/esm/features.js +2 -1
  88. package/esm/features.js.map +1 -1
  89. package/esm/server/client.d.ts +128 -48
  90. package/esm/server/client.d.ts.map +1 -1
  91. package/esm/server/client.js +378 -145
  92. package/esm/server/client.js.map +1 -1
  93. package/esm/server/transport.d.ts +3 -0
  94. package/esm/server/transport.d.ts.map +1 -1
  95. package/esm/server/transport.js +30 -4
  96. package/esm/server/transport.js.map +1 -1
  97. package/esm/utils/base64.d.ts.map +1 -1
  98. package/esm/utils/base64.js +33 -22
  99. package/esm/utils/base64.js.map +1 -1
  100. package/esm/utils/hex.d.ts +1 -1
  101. package/esm/utils/hex.d.ts.map +1 -1
  102. package/esm/utils/hex.js +12 -14
  103. package/esm/utils/hex.js.map +1 -1
  104. package/esm/utils/index.d.ts +1 -1
  105. package/esm/utils/index.d.ts.map +1 -1
  106. package/esm/utils/index.js +1 -1
  107. package/esm/utils/index.js.map +1 -1
  108. package/esm/utils/sequence-equal.d.ts +2 -0
  109. package/esm/utils/sequence-equal.d.ts.map +1 -0
  110. package/esm/utils/sequence-equal.js +12 -0
  111. package/esm/utils/sequence-equal.js.map +1 -0
  112. package/package.json +13 -15
  113. package/src/adb.ts +22 -6
  114. package/src/banner.ts +1 -0
  115. package/src/commands/power.ts +7 -5
  116. package/src/commands/reverse.ts +31 -18
  117. package/src/commands/subprocess/command.ts +3 -3
  118. package/src/commands/subprocess/protocols/none.ts +2 -2
  119. package/src/commands/subprocess/protocols/shell.ts +21 -100
  120. package/src/commands/subprocess/protocols/types.ts +2 -2
  121. package/src/commands/subprocess/utils.ts +1 -1
  122. package/src/commands/sync/list.ts +2 -4
  123. package/src/commands/sync/pull.ts +2 -2
  124. package/src/commands/sync/push.ts +7 -10
  125. package/src/commands/sync/request.ts +32 -32
  126. package/src/commands/sync/response.ts +47 -20
  127. package/src/commands/sync/socket.ts +13 -8
  128. package/src/commands/sync/stat.ts +0 -2
  129. package/src/commands/sync/sync.ts +13 -9
  130. package/src/commands/tcpip.ts +1 -1
  131. package/src/daemon/auth.ts +1 -1
  132. package/src/daemon/crypto.ts +64 -55
  133. package/src/daemon/dispatcher.ts +175 -37
  134. package/src/daemon/packet.ts +21 -14
  135. package/src/daemon/socket.ts +62 -28
  136. package/src/daemon/transport.ts +145 -40
  137. package/src/features.ts +2 -1
  138. package/src/server/client.ts +472 -224
  139. package/src/server/transport.ts +32 -4
  140. package/src/utils/base64.ts +33 -22
  141. package/src/utils/hex.ts +16 -14
  142. package/src/utils/index.ts +1 -1
  143. package/src/utils/sequence-equal.ts +13 -0
  144. package/tsconfig.build.tsbuildinfo +1 -1
  145. package/esm/utils/conditional-variable.d.ts +0 -8
  146. package/esm/utils/conditional-variable.d.ts.map +0 -1
  147. package/esm/utils/conditional-variable.js +0 -34
  148. package/esm/utils/conditional-variable.js.map +0 -1
  149. package/src/utils/conditional-variable.ts +0 -45
@@ -1,8 +1,8 @@
1
1
  import { PromiseResolver } from "@yume-chan/async";
2
- import type { Consumable, ReadableWritablePair } from "@yume-chan/stream-extra";
2
+ import type { ReadableWritablePair } from "@yume-chan/stream-extra";
3
3
  import {
4
4
  AbortController,
5
- ConsumableWritableStream,
5
+ Consumable,
6
6
  WritableStream,
7
7
  } from "@yume-chan/stream-extra";
8
8
  import type { ValueOrPromise } from "@yume-chan/struct";
@@ -26,22 +26,82 @@ import type { AdbPacketData, AdbPacketInit } from "./packet.js";
26
26
  import { AdbCommand, calculateChecksum } from "./packet.js";
27
27
 
28
28
  export const ADB_DAEMON_VERSION_OMIT_CHECKSUM = 0x01000001;
29
+ // https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
30
+ // There are some other feature constants, but some of them are only used by ADB server, not devices (daemons).
31
+ export const ADB_DAEMON_DEFAULT_FEATURES = [
32
+ AdbFeature.ShellV2,
33
+ AdbFeature.Cmd,
34
+ AdbFeature.StatV2,
35
+ AdbFeature.ListV2,
36
+ AdbFeature.FixedPushMkdir,
37
+ "apex",
38
+ AdbFeature.Abb,
39
+ // only tells the client the symlink timestamp issue in `adb push --sync` has been fixed.
40
+ // No special handling required.
41
+ "fixed_push_symlink_timestamp",
42
+ AdbFeature.AbbExec,
43
+ "remount_shell",
44
+ "track_app",
45
+ AdbFeature.SendReceiveV2,
46
+ "sendrecv_v2_brotli",
47
+ "sendrecv_v2_lz4",
48
+ "sendrecv_v2_zstd",
49
+ "sendrecv_v2_dry_run_send",
50
+ AdbFeature.DelayedAck,
51
+ ] as AdbFeature[];
52
+ export const ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE = 32 * 1024 * 1024;
29
53
 
30
54
  export type AdbDaemonConnection = ReadableWritablePair<
31
55
  AdbPacketData,
32
56
  Consumable<AdbPacketInit>
33
57
  >;
34
58
 
35
- interface AdbDaemonAuthenticationOptions {
59
+ export interface AdbDaemonAuthenticationOptions {
36
60
  serial: string;
37
61
  connection: AdbDaemonConnection;
38
62
  credentialStore: AdbCredentialStore;
39
63
  authenticators?: AdbAuthenticator[];
64
+ features?: readonly AdbFeature[];
65
+
66
+ /**
67
+ * The number of bytes the device can send before receiving an ack packet.
68
+ * Using delayed ack can improve the throughput,
69
+ * especially when the device is connected over Wi-Fi (so the latency is higher).
70
+ *
71
+ * Set to 0 or any negative value to disable delayed ack in handshake.
72
+ * Otherwise the value must be in the range of unsigned 32-bit integer.
73
+ *
74
+ * Delayed ack was added in Android 14,
75
+ * this option will be ignored when the device doesn't support it.
76
+ *
77
+ * @default ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE
78
+ */
79
+ initialDelayedAckBytes?: number;
80
+
40
81
  /**
41
- * Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
82
+ * Whether to keep the `connection` open (don't call `writable.close` and `readable.cancel`)
83
+ * when `AdbDaemonTransport.close` is called.
84
+ *
85
+ * Note that when `authenticate` fails,
86
+ * no matter which value this option has,
87
+ * the `connection` is always kept open, so it can be used in another `authenticate` call.
88
+ *
89
+ * @default false
42
90
  */
43
91
  preserveConnection?: boolean | undefined;
44
- debugSlowRead?: boolean | undefined;
92
+
93
+ /**
94
+ * When set, the transport will throw an error when
95
+ * one of the socket readable stalls for this amount of milliseconds.
96
+ *
97
+ * Because ADB is a multiplexed protocol, blocking one socket will also block all other sockets.
98
+ * It's important to always read from all sockets to prevent stalling.
99
+ *
100
+ * This option is helpful to detect bugs in the client code.
101
+ *
102
+ * @default undefined
103
+ */
104
+ readTimeLimit?: number | undefined;
45
105
  }
46
106
 
47
107
  interface AdbDaemonSocketConnectorConstructionOptions {
@@ -50,11 +110,40 @@ interface AdbDaemonSocketConnectorConstructionOptions {
50
110
  version: number;
51
111
  maxPayloadSize: number;
52
112
  banner: string;
113
+ features?: readonly AdbFeature[];
114
+
53
115
  /**
54
- * Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
116
+ * The number of bytes the device can send before receiving an ack packet.
117
+ * Using delayed ack can improve the throughput,
118
+ * especially when the device is connected over Wi-Fi (so the latency is higher).
119
+ *
120
+ * When `features` doesn't include `AdbFeature.DelayedAck`, it must be set to 0. Otherwise,
121
+ * the value must be in the range of unsigned 32-bit integer. If the device enabled
122
+ * delayed ack but the client didn't, the device will throw an error when the client sends
123
+ * the first data packet. And vice versa.
124
+ */
125
+ initialDelayedAckBytes: number;
126
+
127
+ /**
128
+ * Whether to keep the `connection` open (don't call `writable.close` and `readable.cancel`)
129
+ * when `AdbDaemonTransport.close` is called.
130
+ *
131
+ * @default false
55
132
  */
56
133
  preserveConnection?: boolean | undefined;
57
- debugSlowRead?: boolean | undefined;
134
+
135
+ /**
136
+ * When set, the transport will throw an error when
137
+ * one of the socket readable stalls for this amount of milliseconds.
138
+ *
139
+ * Because ADB is a multiplexed protocol, blocking one socket will also block all other sockets.
140
+ * It's important to always read from all sockets to prevent stalling.
141
+ *
142
+ * This option is helpful to detect bugs in the client code.
143
+ *
144
+ * @default undefined
145
+ */
146
+ readTimeLimit?: number | undefined;
58
147
  }
59
148
 
60
149
  export class AdbDaemonTransport implements AdbTransport {
@@ -62,15 +151,17 @@ export class AdbDaemonTransport implements AdbTransport {
62
151
  * Authenticates the connection and creates an `AdbDaemonTransport` instance
63
152
  * that can be used by `Adb` class.
64
153
  *
65
- * If an authentication process failed, it's possible to call `authenticate` again
66
- * on the same connection. Because every time the device receives a `CNXN` packet,
67
- * it resets all internal state, and starts a new authentication process.
154
+ * If an authentication process failed,
155
+ * no matter which value the `preserveConnection` option has,
156
+ * the `connection` is always kept open, so it can be used in another `authenticate` call.
68
157
  */
69
158
  static async authenticate({
70
159
  serial,
71
160
  connection,
72
161
  credentialStore,
73
162
  authenticators = ADB_DEFAULT_AUTHENTICATORS,
163
+ features = ADB_DAEMON_DEFAULT_FEATURES,
164
+ initialDelayedAckBytes = ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE,
74
165
  ...options
75
166
  }: AdbDaemonAuthenticationOptions): Promise<AdbDaemonTransport> {
76
167
  // Initially, set to highest-supported version and payload size.
@@ -124,11 +215,10 @@ export class AdbDaemonTransport implements AdbTransport {
124
215
  )
125
216
  .then(
126
217
  () => {
127
- if (resolver.state === "running") {
128
- resolver.reject(
129
- new Error("Connection closed unexpectedly"),
130
- );
131
- }
218
+ // If `resolver` is already settled, call `reject` won't do anything.
219
+ resolver.reject(
220
+ new Error("Connection closed unexpectedly"),
221
+ );
132
222
  },
133
223
  (e) => {
134
224
  resolver.reject(e);
@@ -141,41 +231,31 @@ export class AdbDaemonTransport implements AdbTransport {
141
231
  // Because we don't know if the device needs it or not.
142
232
  (init as AdbPacketInit).checksum = calculateChecksum(init.payload);
143
233
  (init as AdbPacketInit).magic = init.command ^ 0xffffffff;
144
- await ConsumableWritableStream.write(writer, init as AdbPacketInit);
234
+ await Consumable.WritableStream.write(
235
+ writer,
236
+ init as AdbPacketInit,
237
+ );
238
+ }
239
+
240
+ const actualFeatures = features.slice();
241
+ if (initialDelayedAckBytes <= 0) {
242
+ const index = features.indexOf(AdbFeature.DelayedAck);
243
+ if (index !== -1) {
244
+ actualFeatures.splice(index, 1);
245
+ }
145
246
  }
146
247
 
147
248
  let banner: string;
148
249
  try {
149
- // https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
150
- // There are some other feature constants, but some of them are only used by ADB server, not devices (daemons).
151
- const features = [
152
- AdbFeature.ShellV2,
153
- AdbFeature.Cmd,
154
- AdbFeature.StatV2,
155
- AdbFeature.ListV2,
156
- AdbFeature.FixedPushMkdir,
157
- "apex",
158
- AdbFeature.Abb,
159
- // only tells the client the symlink timestamp issue in `adb push --sync` has been fixed.
160
- // No special handling required.
161
- "fixed_push_symlink_timestamp",
162
- AdbFeature.AbbExec,
163
- "remount_shell",
164
- "track_app",
165
- AdbFeature.SendReceiveV2,
166
- "sendrecv_v2_brotli",
167
- "sendrecv_v2_lz4",
168
- "sendrecv_v2_zstd",
169
- "sendrecv_v2_dry_run_send",
170
- ].join(",");
171
-
172
250
  await sendPacket({
173
251
  command: AdbCommand.Connect,
174
252
  arg0: version,
175
253
  arg1: maxPayloadSize,
176
254
  // The terminating `;` is required in formal definition
177
255
  // But ADB daemon (all versions) can still work without it
178
- payload: encodeUtf8(`host::features=${features}`),
256
+ payload: encodeUtf8(
257
+ `host::features=${actualFeatures.join(",")}`,
258
+ ),
179
259
  });
180
260
 
181
261
  banner = await resolver.promise;
@@ -195,6 +275,8 @@ export class AdbDaemonTransport implements AdbTransport {
195
275
  version,
196
276
  maxPayloadSize,
197
277
  banner,
278
+ features: actualFeatures,
279
+ initialDelayedAckBytes,
198
280
  ...options,
199
281
  });
200
282
  }
@@ -229,16 +311,38 @@ export class AdbDaemonTransport implements AdbTransport {
229
311
  return this.#dispatcher.disconnected;
230
312
  }
231
313
 
314
+ #clientFeatures: readonly AdbFeature[];
315
+ get clientFeatures() {
316
+ return this.#clientFeatures;
317
+ }
318
+
232
319
  constructor({
233
320
  serial,
234
321
  connection,
235
322
  version,
236
323
  banner,
324
+ features = ADB_DAEMON_DEFAULT_FEATURES,
325
+ initialDelayedAckBytes,
237
326
  ...options
238
327
  }: AdbDaemonSocketConnectorConstructionOptions) {
239
328
  this.#serial = serial;
240
329
  this.#connection = connection;
241
330
  this.#banner = AdbBanner.parse(banner);
331
+ this.#clientFeatures = features;
332
+
333
+ if (features.includes(AdbFeature.DelayedAck)) {
334
+ if (initialDelayedAckBytes <= 0) {
335
+ throw new TypeError(
336
+ "`initialDelayedAckBytes` must be greater than 0 when DelayedAck feature is enabled.",
337
+ );
338
+ }
339
+
340
+ if (!this.#banner.features.includes(AdbFeature.DelayedAck)) {
341
+ initialDelayedAckBytes = 0;
342
+ }
343
+ } else {
344
+ initialDelayedAckBytes = 0;
345
+ }
242
346
 
243
347
  let calculateChecksum: boolean;
244
348
  let appendNullToServiceString: boolean;
@@ -253,6 +357,7 @@ export class AdbDaemonTransport implements AdbTransport {
253
357
  this.#dispatcher = new AdbPacketDispatcher(connection, {
254
358
  calculateChecksum,
255
359
  appendNullToServiceString,
360
+ initialDelayedAckBytes,
256
361
  ...options,
257
362
  });
258
363
 
package/src/features.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // The order follows
2
- // https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
2
+ // https://cs.android.com/android/platform/superproject/+/master:packages/modules/adb/transport.cpp;l=77;drc=6d14d35d0241f6fee145f8e54ffd77252e8d29fd
3
3
  export enum AdbFeature {
4
4
  ShellV2 = "shell_v2",
5
5
  Cmd = "cmd",
@@ -9,4 +9,5 @@ export enum AdbFeature {
9
9
  Abb = "abb",
10
10
  AbbExec = "abb_exec",
11
11
  SendReceiveV2 = "sendrecv_v2",
12
+ DelayedAck = "delayed_ack",
12
13
  }