@yume-chan/adb 0.0.10 → 0.0.13

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 (342) hide show
  1. package/CHANGELOG.json +50 -0
  2. package/CHANGELOG.md +29 -1
  3. package/LICENSE +1 -1
  4. package/README.md +71 -51
  5. package/esm/adb.d.ts +48 -0
  6. package/esm/adb.d.ts.map +1 -0
  7. package/esm/adb.js +149 -136
  8. package/esm/adb.js.map +1 -1
  9. package/{dts → esm}/auth.d.ts +11 -11
  10. package/esm/auth.d.ts.map +1 -0
  11. package/esm/auth.js +72 -110
  12. package/esm/auth.js.map +1 -1
  13. package/esm/backend.d.ts +9 -0
  14. package/esm/backend.d.ts.map +1 -0
  15. package/{dts → esm}/commands/base.d.ts +1 -1
  16. package/esm/commands/base.d.ts.map +1 -0
  17. package/esm/commands/base.js +1 -0
  18. package/esm/commands/base.js.map +1 -1
  19. package/{dts → esm}/commands/framebuffer.d.ts +3 -3
  20. package/esm/commands/framebuffer.d.ts.map +1 -0
  21. package/esm/commands/framebuffer.js +15 -18
  22. package/esm/commands/framebuffer.js.map +1 -1
  23. package/esm/commands/index.d.ts +9 -0
  24. package/esm/commands/index.d.ts.map +1 -0
  25. package/esm/commands/index.js +8 -8
  26. package/esm/commands/index.js.map +1 -1
  27. package/esm/commands/install.d.ts +4 -0
  28. package/esm/commands/install.d.ts.map +1 -0
  29. package/esm/commands/install.js +19 -13
  30. package/esm/commands/install.js.map +1 -1
  31. package/esm/commands/power.d.ts +23 -0
  32. package/esm/commands/power.d.ts.map +1 -0
  33. package/esm/commands/power.js +45 -0
  34. package/esm/commands/power.js.map +1 -0
  35. package/{dts → esm}/commands/reverse.d.ts +3 -3
  36. package/esm/commands/reverse.d.ts.map +1 -0
  37. package/esm/commands/reverse.js +49 -60
  38. package/esm/commands/reverse.js.map +1 -1
  39. package/esm/commands/subprocess/index.d.ts +56 -0
  40. package/esm/commands/subprocess/index.d.ts.map +1 -0
  41. package/esm/commands/subprocess/index.js +88 -0
  42. package/esm/commands/subprocess/index.js.map +1 -0
  43. package/esm/commands/subprocess/legacy.d.ts +35 -0
  44. package/esm/commands/subprocess/legacy.d.ts.map +1 -0
  45. package/esm/commands/subprocess/legacy.js +53 -0
  46. package/esm/commands/subprocess/legacy.js.map +1 -0
  47. package/esm/commands/subprocess/protocol.d.ts +39 -0
  48. package/esm/commands/subprocess/protocol.d.ts.map +1 -0
  49. package/esm/commands/subprocess/protocol.js +157 -0
  50. package/esm/commands/subprocess/protocol.js.map +1 -0
  51. package/esm/commands/subprocess/protocols/index.d.ts +4 -0
  52. package/esm/commands/subprocess/protocols/index.d.ts.map +1 -0
  53. package/esm/commands/subprocess/protocols/index.js +4 -0
  54. package/esm/commands/subprocess/protocols/index.js.map +1 -0
  55. package/esm/commands/subprocess/protocols/none.d.ts +35 -0
  56. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -0
  57. package/esm/commands/subprocess/protocols/none.js +53 -0
  58. package/esm/commands/subprocess/protocols/none.js.map +1 -0
  59. package/esm/commands/subprocess/protocols/shell.d.ts +39 -0
  60. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -0
  61. package/esm/commands/subprocess/protocols/shell.js +155 -0
  62. package/esm/commands/subprocess/protocols/shell.js.map +1 -0
  63. package/esm/commands/subprocess/protocols/types.d.ts +50 -0
  64. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -0
  65. package/esm/commands/{shell → subprocess/protocols}/types.js +0 -0
  66. package/esm/commands/subprocess/protocols/types.js.map +1 -0
  67. package/esm/commands/subprocess/types.d.ts +50 -0
  68. package/esm/commands/subprocess/types.d.ts.map +1 -0
  69. package/esm/commands/subprocess/types.js +2 -0
  70. package/esm/commands/{shell → subprocess}/types.js.map +1 -1
  71. package/{dts/commands/shell → esm/commands/subprocess}/utils.d.ts +0 -0
  72. package/esm/commands/subprocess/utils.d.ts.map +1 -0
  73. package/esm/commands/{shell → subprocess}/utils.js +0 -0
  74. package/esm/commands/subprocess/utils.js.map +1 -0
  75. package/esm/commands/sync/index.d.ts +8 -0
  76. package/esm/commands/sync/index.d.ts.map +1 -0
  77. package/esm/commands/sync/index.js +7 -7
  78. package/esm/commands/sync/index.js.map +1 -1
  79. package/esm/commands/sync/list.d.ts +41 -0
  80. package/esm/commands/sync/list.d.ts.map +1 -0
  81. package/esm/commands/sync/list.js +52 -20
  82. package/esm/commands/sync/list.js.map +1 -1
  83. package/esm/commands/sync/pull.d.ts +11 -0
  84. package/esm/commands/sync/pull.d.ts.map +1 -0
  85. package/esm/commands/sync/pull.js +17 -11
  86. package/esm/commands/sync/pull.js.map +1 -1
  87. package/esm/commands/sync/push.d.ts +8 -0
  88. package/esm/commands/sync/push.d.ts.map +1 -0
  89. package/esm/commands/sync/push.js +19 -37
  90. package/esm/commands/sync/push.js.map +1 -1
  91. package/{dts → esm}/commands/sync/request.d.ts +4 -3
  92. package/esm/commands/sync/request.d.ts.map +1 -0
  93. package/esm/commands/sync/request.js +25 -27
  94. package/esm/commands/sync/request.js.map +1 -1
  95. package/{dts → esm}/commands/sync/response.d.ts +4 -3
  96. package/esm/commands/sync/response.d.ts.map +1 -0
  97. package/esm/commands/sync/response.js +17 -20
  98. package/esm/commands/sync/response.js.map +1 -1
  99. package/{dts → esm}/commands/sync/stat.d.ts +16 -4
  100. package/esm/commands/sync/stat.d.ts.map +1 -0
  101. package/esm/commands/sync/stat.js +38 -30
  102. package/esm/commands/sync/stat.js.map +1 -1
  103. package/esm/commands/sync/sync.d.ts +61 -0
  104. package/esm/commands/sync/sync.d.ts.map +1 -0
  105. package/esm/commands/sync/sync.js +122 -89
  106. package/esm/commands/sync/sync.js.map +1 -1
  107. package/{dts → esm}/commands/tcpip.d.ts +1 -1
  108. package/{dts → esm}/commands/tcpip.d.ts.map +1 -1
  109. package/esm/commands/tcpip.js +14 -19
  110. package/esm/commands/tcpip.js.map +1 -1
  111. package/esm/crypto.d.ts +37 -0
  112. package/esm/crypto.d.ts.map +1 -0
  113. package/esm/crypto.js +77 -62
  114. package/esm/crypto.js.map +1 -1
  115. package/esm/features.d.ts +8 -0
  116. package/esm/features.d.ts.map +1 -0
  117. package/esm/features.js +7 -3
  118. package/esm/features.js.map +1 -1
  119. package/esm/index.d.ts +11 -0
  120. package/esm/index.d.ts.map +1 -0
  121. package/esm/index.js +10 -10
  122. package/esm/index.js.map +1 -1
  123. package/esm/packet.d.ts +47 -0
  124. package/esm/packet.d.ts.map +1 -0
  125. package/esm/packet.js +27 -52
  126. package/esm/packet.js.map +1 -1
  127. package/esm/socket/controller.d.ts +37 -0
  128. package/esm/socket/controller.d.ts.map +1 -0
  129. package/esm/socket/controller.js +52 -66
  130. package/esm/socket/controller.js.map +1 -1
  131. package/esm/socket/dispatcher.d.ts +44 -0
  132. package/esm/socket/dispatcher.d.ts.map +1 -0
  133. package/esm/socket/dispatcher.js +146 -165
  134. package/esm/socket/dispatcher.js.map +1 -1
  135. package/esm/socket/index.d.ts +3 -0
  136. package/esm/socket/index.d.ts.map +1 -0
  137. package/esm/socket/index.js +2 -6
  138. package/esm/socket/index.js.map +1 -1
  139. package/esm/socket/socket.d.ts +46 -0
  140. package/esm/socket/socket.d.ts.map +1 -0
  141. package/esm/socket/socket.js +77 -13
  142. package/esm/socket/socket.js.map +1 -1
  143. package/esm/stream/buffered.d.ts +35 -0
  144. package/esm/stream/buffered.d.ts.map +1 -0
  145. package/esm/stream/buffered.js +123 -0
  146. package/esm/stream/buffered.js.map +1 -0
  147. package/esm/stream/detect.bak.d.ts +240 -0
  148. package/esm/stream/detect.bak.d.ts.map +1 -0
  149. package/esm/stream/detect.bak.js +60 -0
  150. package/esm/stream/detect.bak.js.map +1 -0
  151. package/esm/stream/detect.d.ts +2 -0
  152. package/esm/stream/detect.d.ts.map +1 -0
  153. package/esm/stream/detect.js +5 -0
  154. package/esm/stream/detect.js.map +1 -0
  155. package/esm/stream/detect.native.d.ts +240 -0
  156. package/esm/stream/detect.native.d.ts.map +1 -0
  157. package/esm/stream/detect.native.js +60 -0
  158. package/esm/stream/detect.native.js.map +1 -0
  159. package/esm/stream/detect.polyfill.d.ts +18 -0
  160. package/esm/stream/detect.polyfill.d.ts.map +1 -0
  161. package/esm/stream/detect.polyfill.js +9 -0
  162. package/esm/stream/detect.polyfill.js.map +1 -0
  163. package/esm/stream/index.d.ts +4 -0
  164. package/esm/stream/index.d.ts.map +1 -0
  165. package/esm/stream/index.js +3 -2
  166. package/esm/stream/index.js.map +1 -1
  167. package/esm/stream/transform.d.ts +98 -0
  168. package/esm/stream/transform.d.ts.map +1 -0
  169. package/esm/stream/transform.js +402 -0
  170. package/esm/stream/transform.js.map +1 -0
  171. package/{dts → esm}/utils/auto-reset-event.d.ts +1 -1
  172. package/esm/utils/auto-reset-event.d.ts.map +1 -0
  173. package/esm/utils/auto-reset-event.js +2 -1
  174. package/esm/utils/auto-reset-event.js.map +1 -1
  175. package/esm/utils/base64.d.ts +5 -0
  176. package/esm/utils/base64.d.ts.map +1 -0
  177. package/esm/utils/base64.js +116 -80
  178. package/esm/utils/base64.js.map +1 -1
  179. package/esm/utils/encoding.d.ts +3 -0
  180. package/esm/utils/encoding.d.ts.map +1 -0
  181. package/esm/utils/encoding.js +1 -1
  182. package/esm/utils/encoding.js.map +1 -1
  183. package/esm/utils/index.d.ts +4 -0
  184. package/esm/utils/index.d.ts.map +1 -0
  185. package/esm/utils/index.js +3 -5
  186. package/esm/utils/index.js.map +1 -1
  187. package/package.json +50 -44
  188. package/src/adb.ts +177 -150
  189. package/src/auth.ts +37 -30
  190. package/src/backend.ts +3 -12
  191. package/src/commands/base.ts +1 -1
  192. package/src/commands/framebuffer.ts +4 -4
  193. package/src/commands/index.ts +8 -8
  194. package/src/commands/install.ts +24 -15
  195. package/src/commands/power.ts +54 -0
  196. package/src/commands/reverse.ts +10 -8
  197. package/src/commands/subprocess/index.ts +139 -0
  198. package/src/commands/subprocess/protocols/index.ts +3 -0
  199. package/src/commands/subprocess/protocols/none.ts +68 -0
  200. package/src/commands/subprocess/protocols/shell.ts +190 -0
  201. package/src/commands/subprocess/protocols/types.ts +59 -0
  202. package/src/commands/{shell → subprocess}/utils.ts +0 -0
  203. package/src/commands/sync/index.ts +7 -7
  204. package/src/commands/sync/list.ts +56 -9
  205. package/src/commands/sync/pull.ts +27 -18
  206. package/src/commands/sync/push.ts +25 -30
  207. package/src/commands/sync/request.ts +8 -7
  208. package/src/commands/sync/response.ts +12 -6
  209. package/src/commands/sync/stat.ts +43 -14
  210. package/src/commands/sync/sync.ts +102 -34
  211. package/src/commands/tcpip.ts +3 -3
  212. package/src/crypto.ts +95 -84
  213. package/src/features.ts +6 -2
  214. package/src/index.ts +11 -10
  215. package/src/packet.ts +49 -53
  216. package/src/socket/dispatcher.ts +110 -109
  217. package/src/socket/index.ts +2 -6
  218. package/src/socket/socket.ts +125 -17
  219. package/src/stream/buffered.ts +147 -0
  220. package/src/stream/detect.native.ts +362 -0
  221. package/src/stream/detect.polyfill.ts +28 -0
  222. package/src/stream/detect.ts +5 -0
  223. package/src/stream/index.ts +3 -2
  224. package/src/stream/transform.ts +483 -0
  225. package/src/utils/auto-reset-event.ts +1 -1
  226. package/src/utils/base64.ts +161 -129
  227. package/src/utils/index.ts +3 -5
  228. package/tsconfig.build.json +3 -0
  229. package/tsconfig.build.tsbuildinfo +1 -0
  230. package/tsconfig.test.json +10 -0
  231. package/dts/adb.d.ts +0 -46
  232. package/dts/adb.d.ts.map +0 -1
  233. package/dts/auth.d.ts.map +0 -1
  234. package/dts/backend.d.ts +0 -13
  235. package/dts/backend.d.ts.map +0 -1
  236. package/dts/commands/base.d.ts.map +0 -1
  237. package/dts/commands/demo-mode.d.ts +0 -41
  238. package/dts/commands/demo-mode.d.ts.map +0 -1
  239. package/dts/commands/framebuffer.d.ts.map +0 -1
  240. package/dts/commands/index.d.ts +0 -9
  241. package/dts/commands/index.d.ts.map +0 -1
  242. package/dts/commands/install.d.ts +0 -3
  243. package/dts/commands/install.d.ts.map +0 -1
  244. package/dts/commands/reverse.d.ts.map +0 -1
  245. package/dts/commands/shell/index.d.ts +0 -46
  246. package/dts/commands/shell/index.d.ts.map +0 -1
  247. package/dts/commands/shell/legacy.d.ts +0 -38
  248. package/dts/commands/shell/legacy.d.ts.map +0 -1
  249. package/dts/commands/shell/protocol.d.ts +0 -45
  250. package/dts/commands/shell/protocol.d.ts.map +0 -1
  251. package/dts/commands/shell/types.d.ts +0 -47
  252. package/dts/commands/shell/types.d.ts.map +0 -1
  253. package/dts/commands/shell/utils.d.ts.map +0 -1
  254. package/dts/commands/sync/index.d.ts +0 -8
  255. package/dts/commands/sync/index.d.ts.map +0 -1
  256. package/dts/commands/sync/list.d.ts +0 -17
  257. package/dts/commands/sync/list.d.ts.map +0 -1
  258. package/dts/commands/sync/pull.d.ts +0 -11
  259. package/dts/commands/sync/pull.d.ts.map +0 -1
  260. package/dts/commands/sync/push.d.ts +0 -8
  261. package/dts/commands/sync/push.d.ts.map +0 -1
  262. package/dts/commands/sync/request.d.ts.map +0 -1
  263. package/dts/commands/sync/response.d.ts.map +0 -1
  264. package/dts/commands/sync/stat.d.ts.map +0 -1
  265. package/dts/commands/sync/sync.d.ts +0 -68
  266. package/dts/commands/sync/sync.d.ts.map +0 -1
  267. package/dts/crypto.d.ts +0 -24
  268. package/dts/crypto.d.ts.map +0 -1
  269. package/dts/features.d.ts +0 -6
  270. package/dts/features.d.ts.map +0 -1
  271. package/dts/index.d.ts +0 -11
  272. package/dts/index.d.ts.map +0 -1
  273. package/dts/packet.d.ts +0 -27
  274. package/dts/packet.d.ts.map +0 -1
  275. package/dts/socket/close-event-emitter.d.ts +0 -8
  276. package/dts/socket/close-event-emitter.d.ts.map +0 -1
  277. package/dts/socket/controller.d.ts +0 -43
  278. package/dts/socket/controller.d.ts.map +0 -1
  279. package/dts/socket/data-event-emitter.d.ts +0 -10
  280. package/dts/socket/data-event-emitter.d.ts.map +0 -1
  281. package/dts/socket/dispatcher.d.ts +0 -53
  282. package/dts/socket/dispatcher.d.ts.map +0 -1
  283. package/dts/socket/index.d.ts +0 -7
  284. package/dts/socket/index.d.ts.map +0 -1
  285. package/dts/socket/logger.d.ts +0 -9
  286. package/dts/socket/logger.d.ts.map +0 -1
  287. package/dts/socket/socket.d.ts +0 -22
  288. package/dts/socket/socket.d.ts.map +0 -1
  289. package/dts/stream/buffered-stream.d.ts +0 -28
  290. package/dts/stream/buffered-stream.d.ts.map +0 -1
  291. package/dts/stream/index.d.ts +0 -3
  292. package/dts/stream/index.d.ts.map +0 -1
  293. package/dts/stream/stream.d.ts +0 -15
  294. package/dts/stream/stream.d.ts.map +0 -1
  295. package/dts/utils/auto-reset-event.d.ts.map +0 -1
  296. package/dts/utils/base64.d.ts +0 -7
  297. package/dts/utils/base64.d.ts.map +0 -1
  298. package/dts/utils/chunk.d.ts +0 -3
  299. package/dts/utils/chunk.d.ts.map +0 -1
  300. package/dts/utils/encoding.d.ts +0 -3
  301. package/dts/utils/encoding.d.ts.map +0 -1
  302. package/dts/utils/event-queue.d.ts +0 -35
  303. package/dts/utils/event-queue.d.ts.map +0 -1
  304. package/dts/utils/index.d.ts +0 -6
  305. package/dts/utils/index.d.ts.map +0 -1
  306. package/esm/commands/demo-mode.js +0 -170
  307. package/esm/commands/demo-mode.js.map +0 -1
  308. package/esm/commands/shell/index.js +0 -62
  309. package/esm/commands/shell/index.js.map +0 -1
  310. package/esm/commands/shell/legacy.js +0 -49
  311. package/esm/commands/shell/legacy.js.map +0 -1
  312. package/esm/commands/shell/protocol.js +0 -106
  313. package/esm/commands/shell/protocol.js.map +0 -1
  314. package/esm/commands/shell/utils.js.map +0 -1
  315. package/esm/socket/close-event-emitter.js +0 -28
  316. package/esm/socket/close-event-emitter.js.map +0 -1
  317. package/esm/socket/data-event-emitter.js +0 -25
  318. package/esm/socket/data-event-emitter.js.map +0 -1
  319. package/esm/socket/logger.js +0 -2
  320. package/esm/socket/logger.js.map +0 -1
  321. package/esm/stream/buffered-stream.js +0 -67
  322. package/esm/stream/buffered-stream.js.map +0 -1
  323. package/esm/stream/stream.js +0 -41
  324. package/esm/stream/stream.js.map +0 -1
  325. package/esm/utils/chunk.js +0 -70
  326. package/esm/utils/chunk.js.map +0 -1
  327. package/esm/utils/event-queue.js +0 -104
  328. package/esm/utils/event-queue.js.map +0 -1
  329. package/src/commands/demo-mode.ts +0 -160
  330. package/src/commands/shell/index.ts +0 -87
  331. package/src/commands/shell/legacy.ts +0 -59
  332. package/src/commands/shell/protocol.ts +0 -121
  333. package/src/commands/shell/types.ts +0 -55
  334. package/src/socket/close-event-emitter.ts +0 -28
  335. package/src/socket/controller.ts +0 -105
  336. package/src/socket/data-event-emitter.ts +0 -27
  337. package/src/socket/logger.ts +0 -12
  338. package/src/stream/buffered-stream.ts +0 -92
  339. package/src/stream/stream.ts +0 -50
  340. package/src/utils/chunk.ts +0 -70
  341. package/src/utils/encoding.ts +0 -13
  342. package/src/utils/event-queue.ts +0 -145
package/src/adb.ts CHANGED
@@ -1,12 +1,13 @@
1
+ // cspell: ignore libusb
2
+
1
3
  import { PromiseResolver } from '@yume-chan/async';
2
- import { DisposableList } from '@yume-chan/event';
3
- import { AdbAuthenticationHandler, AdbCredentialStore, AdbDefaultAuthenticators } from './auth';
4
- import { AdbBackend } from './backend';
5
- import { AdbChildProcess, AdbDemoMode, AdbFrameBuffer, AdbReverseCommand, AdbSync, AdbTcpIpCommand, escapeArg, framebuffer, install } from './commands';
6
- import { AdbFeatures } from './features';
7
- import { AdbCommand } from './packet';
8
- import { AdbLogger, AdbPacketDispatcher, AdbSocket } from './socket';
9
- import { decodeUtf8 } from "./utils";
4
+ import { AdbAuthenticationProcessor, ADB_DEFAULT_AUTHENTICATORS, type AdbCredentialStore } from './auth.js';
5
+ import { AdbPower, AdbReverseCommand, AdbSubprocess, AdbSync, AdbTcpIpCommand, escapeArg, framebuffer, install, type AdbFrameBuffer } from './commands/index.js';
6
+ import { AdbFeatures } from './features.js';
7
+ import { AdbCommand, calculateChecksum, type AdbPacketData, type AdbPacketInit } from './packet.js';
8
+ import { AdbPacketDispatcher, type AdbSocket } from './socket/index.js';
9
+ import { AbortController, DecodeUtf8Stream, GatherStringStream, WritableStream, type ReadableWritablePair } from "./stream/index.js";
10
+ import { decodeUtf8, encodeUtf8 } from "./utils/index.js";
10
11
 
11
12
  export enum AdbPropKey {
12
13
  Product = 'ro.product.name',
@@ -15,19 +16,123 @@ export enum AdbPropKey {
15
16
  Features = 'features',
16
17
  }
17
18
 
19
+ export const VERSION_OMIT_CHECKSUM = 0x01000001;
20
+
18
21
  export class Adb {
19
- private readonly _backend: AdbBackend;
22
+ /**
23
+ * It's possible to call `authenticate` multiple times on a single connection,
24
+ * every time the device receives a `CNXN` packet, it resets its internal state,
25
+ * and starts a new authentication process.
26
+ */
27
+ public static async authenticate(
28
+ connection: ReadableWritablePair<AdbPacketData, AdbPacketInit>,
29
+ credentialStore: AdbCredentialStore,
30
+ authenticators = ADB_DEFAULT_AUTHENTICATORS,
31
+ ): Promise<Adb> {
32
+ // Initially, set to highest-supported version and payload size.
33
+ let version = 0x01000001;
34
+ let maxPayloadSize = 0x100000;
20
35
 
21
- public get backend(): AdbBackend { return this._backend; }
36
+ const resolver = new PromiseResolver<string>();
37
+ const authProcessor = new AdbAuthenticationProcessor(authenticators, credentialStore);
38
+
39
+ // Here is similar to `AdbPacketDispatcher`,
40
+ // But the received packet types and send packet processing are different.
41
+ const abortController = new AbortController();
42
+ const pipe = connection.readable
43
+ .pipeTo(new WritableStream({
44
+ async write(packet) {
45
+ switch (packet.command) {
46
+ case AdbCommand.Connect:
47
+ version = Math.min(version, packet.arg0);
48
+ maxPayloadSize = Math.min(maxPayloadSize, packet.arg1);
49
+ resolver.resolve(decodeUtf8(packet.payload));
50
+ break;
51
+ case AdbCommand.Auth:
52
+ const response = await authProcessor.process(packet);
53
+ await sendPacket(response);
54
+ break;
55
+ default:
56
+ // Maybe the previous ADB session exited without reading all packets,
57
+ // so they are still waiting in OS internal buffer.
58
+ // Just ignore them.
59
+ // Because a `Connect` packet will reset the device,
60
+ // Eventually there will be `Connect` and `Auth` response packets.
61
+ break;
62
+ }
63
+ }
64
+ }), {
65
+ preventCancel: true,
66
+ signal: abortController.signal,
67
+ })
68
+ .catch((e) => {
69
+ resolver.reject(e);
70
+ });
22
71
 
23
- private readonly packetDispatcher: AdbPacketDispatcher;
72
+ const writer = connection.writable.getWriter();
73
+ async function sendPacket(init: AdbPacketData) {
74
+ // Always send checksum in auth steps
75
+ // Because we don't know if the device needs it or not.
76
+ await writer.write(calculateChecksum(init));
77
+ }
24
78
 
25
- public get onDisconnected() { return this.backend.onDisconnected; }
79
+ try {
80
+ // https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
81
+ // There are some other feature constants, but some of them are only used by ADB server, not devices.
82
+ const features = [
83
+ AdbFeatures.ShellV2,
84
+ AdbFeatures.Cmd,
85
+ AdbFeatures.StatV2,
86
+ AdbFeatures.ListV2,
87
+ 'fixed_push_mkdir',
88
+ 'apex',
89
+ 'abb',
90
+ 'fixed_push_symlink_timestamp',
91
+ 'abb_exec',
92
+ 'remount_shell',
93
+ 'track_app',
94
+ 'sendrecv_v2',
95
+ 'sendrecv_v2_brotli',
96
+ 'sendrecv_v2_lz4',
97
+ 'sendrecv_v2_zstd',
98
+ 'sendrecv_v2_dry_run_send',
99
+ ].join(',');
100
+
101
+ await sendPacket({
102
+ command: AdbCommand.Connect,
103
+ arg0: version,
104
+ arg1: maxPayloadSize,
105
+ // The terminating `;` is required in formal definition
106
+ // But ADB daemon (all versions) can still work without it
107
+ payload: encodeUtf8(`host::features=${features};`),
108
+ });
109
+
110
+ const banner = await resolver.promise;
111
+
112
+ // Stop piping before creating `Adb` object
113
+ // Because `AdbPacketDispatcher` will lock the streams when initializing
114
+ abortController.abort();
115
+ writer.releaseLock();
116
+
117
+ // Wait until pipe stops (`ReadableStream` lock released)
118
+ await pipe;
119
+
120
+ return new Adb(
121
+ connection,
122
+ version,
123
+ maxPayloadSize,
124
+ banner,
125
+ );
126
+ } catch (e) {
127
+ abortController.abort();
128
+ writer.releaseLock();
129
+ throw e;
130
+ }
131
+ }
26
132
 
27
- private _connected = false;
28
- public get connected() { return this._connected; }
133
+ private readonly packetDispatcher: AdbPacketDispatcher;
29
134
 
30
- public get name() { return this.backend.name; }
135
+ public get disconnected() { return this.packetDispatcher.disconnected; }
31
136
 
32
137
  private _protocolVersion: number | undefined;
33
138
  public get protocolVersion() { return this._protocolVersion; }
@@ -44,121 +149,44 @@ export class Adb {
44
149
  private _features: AdbFeatures[] | undefined;
45
150
  public get features() { return this._features; }
46
151
 
47
- public readonly tcpip: AdbTcpIpCommand;
48
-
152
+ public readonly subprocess: AdbSubprocess;
153
+ public readonly power: AdbPower;
49
154
  public readonly reverse: AdbReverseCommand;
155
+ public readonly tcpip: AdbTcpIpCommand;
50
156
 
51
- public readonly demoMode: AdbDemoMode;
52
-
53
- public readonly childProcess: AdbChildProcess;
54
-
55
- public constructor(backend: AdbBackend, logger?: AdbLogger) {
56
- this._backend = backend;
57
- this.packetDispatcher = new AdbPacketDispatcher(backend, logger);
58
-
59
- this.tcpip = new AdbTcpIpCommand(this);
60
- this.reverse = new AdbReverseCommand(this.packetDispatcher);
61
- this.demoMode = new AdbDemoMode(this);
62
- this.childProcess = new AdbChildProcess(this);
63
-
64
- backend.onDisconnected(this.dispose, this);
65
- }
157
+ public constructor(
158
+ connection: ReadableWritablePair<AdbPacketData, AdbPacketInit>,
159
+ version: number,
160
+ maxPayloadSize: number,
161
+ banner: string,
162
+ ) {
163
+ this.parseBanner(banner);
164
+
165
+ let calculateChecksum: boolean;
166
+ let appendNullToServiceString: boolean;
167
+ if (version >= VERSION_OMIT_CHECKSUM) {
168
+ calculateChecksum = false;
169
+ appendNullToServiceString = false;
170
+ } else {
171
+ calculateChecksum = true;
172
+ appendNullToServiceString = true;
173
+ }
66
174
 
67
- public async connect(
68
- credentialStore: AdbCredentialStore,
69
- authenticators = AdbDefaultAuthenticators
70
- ): Promise<void> {
71
- await this.backend.connect?.();
72
- this.packetDispatcher.maxPayloadSize = 0x1000;
73
- this.packetDispatcher.calculateChecksum = true;
74
- this.packetDispatcher.appendNullToServiceString = true;
75
- this.packetDispatcher.start();
76
-
77
- const version = 0x01000001;
78
- const versionNoChecksum = 0x01000001;
79
- const maxPayloadSize = 0x100000;
80
-
81
- const features = [
82
- 'shell_v2',
83
- 'cmd',
84
- AdbFeatures.StatV2,
85
- 'ls_v2',
86
- 'fixed_push_mkdir',
87
- 'apex',
88
- 'abb',
89
- 'fixed_push_symlink_timestamp',
90
- 'abb_exec',
91
- 'remount_shell',
92
- 'track_app',
93
- 'sendrecv_v2',
94
- 'sendrecv_v2_brotli',
95
- 'sendrecv_v2_lz4',
96
- 'sendrecv_v2_zstd',
97
- 'sendrecv_v2_dry_run_send',
98
- ].join(',');
99
-
100
- const resolver = new PromiseResolver<void>();
101
- const authHandler = new AdbAuthenticationHandler(authenticators, credentialStore);
102
- const disposableList = new DisposableList();
103
- disposableList.add(this.packetDispatcher.onPacket(async (e) => {
104
- e.handled = true;
105
-
106
- const { packet } = e;
107
- try {
108
- switch (packet.command) {
109
- case AdbCommand.Connect:
110
- this.packetDispatcher.maxPayloadSize = Math.min(maxPayloadSize, packet.arg1);
111
-
112
- const finalVersion = Math.min(version, packet.arg0);
113
- this._protocolVersion = finalVersion;
114
-
115
- if (finalVersion >= versionNoChecksum) {
116
- this.packetDispatcher.calculateChecksum = false;
117
- // Android prior to 9.0.0 uses char* to parse service string
118
- // thus requires an extra null character
119
- this.packetDispatcher.appendNullToServiceString = false;
120
- }
121
-
122
- this.parseBanner(decodeUtf8(packet.payload!));
123
- resolver.resolve();
124
- break;
125
- case AdbCommand.Auth:
126
- const authPacket = await authHandler.handle(e.packet);
127
- await this.packetDispatcher.sendPacket(authPacket);
128
- break;
129
- case AdbCommand.Close:
130
- // Last connection was interrupted
131
- // Ignore this packet, device will recover
132
- break;
133
- default:
134
- throw new Error('Device not in correct state. Reconnect your device and try again');
135
- }
136
- } catch (e) {
137
- resolver.reject(e);
175
+ this.packetDispatcher = new AdbPacketDispatcher(
176
+ connection,
177
+ {
178
+ calculateChecksum,
179
+ appendNullToServiceString,
180
+ maxPayloadSize,
138
181
  }
139
- }));
140
-
141
- disposableList.add(this.packetDispatcher.onError(e => {
142
- resolver.reject(e);
143
- }));
144
-
145
- // Android prior 9.0.0 requires the null character
146
- // Newer versions can also handle the null character
147
- // The terminating `;` is required in formal definition
148
- // But ADB daemon can also work without it
149
- await this.packetDispatcher.sendPacket(
150
- AdbCommand.Connect,
151
- version,
152
- maxPayloadSize,
153
- `host::features=${features};\0`
154
182
  );
155
183
 
156
- try {
157
- await resolver.promise;
158
- this._connected = true;
159
- } finally {
160
- disposableList.dispose();
161
- }
184
+ this._protocolVersion = version;
185
+
186
+ this.subprocess = new AdbSubprocess(this);
187
+ this.power = new AdbPower(this);
188
+ this.reverse = new AdbReverseCommand(this.packetDispatcher);
189
+ this.tcpip = new AdbTcpIpCommand(this);
162
190
  }
163
191
 
164
192
  private parseBanner(banner: string): void {
@@ -166,7 +194,7 @@ export class Adb {
166
194
 
167
195
  const pieces = banner.split('::');
168
196
  if (pieces.length > 1) {
169
- const props = pieces[1];
197
+ const props = pieces[1]!;
170
198
  for (const prop of props.split(';')) {
171
199
  if (!prop) {
172
200
  continue;
@@ -189,27 +217,42 @@ export class Adb {
189
217
  this._device = value;
190
218
  break;
191
219
  case AdbPropKey.Features:
192
- this._features = value.split(',') as AdbFeatures[];
220
+ this._features = value!.split(',') as AdbFeatures[];
193
221
  break;
194
222
  }
195
223
  }
196
224
  }
197
225
  }
198
226
 
227
+ public async createSocket(service: string): Promise<AdbSocket> {
228
+ return this.packetDispatcher.createSocket(service);
229
+ }
230
+
231
+ public async createSocketAndWait(service: string): Promise<string> {
232
+ const socket = await this.createSocket(service);
233
+ const gatherStream = new GatherStringStream();
234
+ await socket.readable
235
+ .pipeThrough(new DecodeUtf8Stream())
236
+ .pipeTo(gatherStream);
237
+ return gatherStream.result;
238
+ }
239
+
199
240
  public async getProp(key: string): Promise<string> {
200
- const output = await this.childProcess.exec('getprop', key);
201
- return output.trim();
241
+ const stdout = await this.subprocess.spawnAndWaitLegacy(
242
+ ['getprop', key]
243
+ );
244
+ return stdout.trim();
202
245
  }
203
246
 
204
247
  public async rm(...filenames: string[]): Promise<string> {
205
- return await this.childProcess.exec('rm', '-rf', ...filenames.map(arg => escapeArg(arg)));
248
+ const stdout = await this.subprocess.spawnAndWaitLegacy(
249
+ ['rm', '-rf', ...filenames.map(arg => escapeArg(arg))],
250
+ );
251
+ return stdout;
206
252
  }
207
253
 
208
- public async install(
209
- apk: ArrayLike<number> | ArrayBufferLike | AsyncIterable<ArrayBuffer>,
210
- onProgress?: (uploaded: number) => void,
211
- ): Promise<void> {
212
- return await install(this, apk, onProgress);
254
+ public install() {
255
+ return install(this);
213
256
  }
214
257
 
215
258
  public async sync(): Promise<AdbSync> {
@@ -221,23 +264,7 @@ export class Adb {
221
264
  return framebuffer(this);
222
265
  }
223
266
 
224
- public async createSocket(service: string): Promise<AdbSocket> {
225
- return this.packetDispatcher.createSocket(service);
226
- }
227
-
228
- public async createSocketAndReadAll(service: string): Promise<string> {
229
- const socket = await this.createSocket(service);
230
- const resolver = new PromiseResolver<string>();
231
- let result = '';
232
- socket.onData(buffer => {
233
- result += decodeUtf8(buffer);
234
- });
235
- socket.onClose(() => resolver.resolve(result));
236
- return resolver.promise;
237
- }
238
-
239
267
  public async dispose(): Promise<void> {
240
268
  this.packetDispatcher.dispose();
241
- await this.backend.dispose();
242
269
  }
243
270
  }
package/src/auth.ts CHANGED
@@ -1,19 +1,19 @@
1
1
  import { PromiseResolver } from '@yume-chan/async';
2
- import { Disposable } from '@yume-chan/event';
3
- import { ValueOrPromise } from '@yume-chan/struct';
4
- import { calculatePublicKey, calculatePublicKeyLength, sign } from './crypto';
5
- import { AdbCommand, AdbPacket, AdbPacketInit } from './packet';
6
- import { calculateBase64EncodedLength, encodeBase64 } from './utils';
2
+ import type { Disposable } from '@yume-chan/event';
3
+ import type { ValueOrPromise } from '@yume-chan/struct';
4
+ import { calculatePublicKey, calculatePublicKeyLength, sign } from './crypto.js';
5
+ import { AdbCommand, type AdbPacketData } from './packet.js';
6
+ import { calculateBase64EncodedLength, encodeBase64 } from './utils/index.js';
7
7
 
8
- export type AdbKeyIterable = Iterable<ArrayBuffer> | AsyncIterable<ArrayBuffer>;
8
+ export type AdbKeyIterable = Iterable<Uint8Array> | AsyncIterable<Uint8Array>;
9
9
 
10
10
  export interface AdbCredentialStore {
11
11
  /**
12
12
  * Generate and store a RSA private key with modulus length `2048` and public exponent `65537`.
13
13
  *
14
- * The returned `ArrayBuffer` is the private key in PKCS #8 format.
14
+ * The returned `Uint8Array` is the private key in PKCS #8 format.
15
15
  */
16
- generateKey(): ValueOrPromise<ArrayBuffer>;
16
+ generateKey(): ValueOrPromise<Uint8Array>;
17
17
 
18
18
  /**
19
19
  * Synchronously or asynchronously iterate through all stored RSA private keys.
@@ -43,14 +43,14 @@ export interface AdbAuthenticator {
43
43
  */
44
44
  (
45
45
  credentialStore: AdbCredentialStore,
46
- getNextRequest: () => Promise<AdbPacket>
47
- ): AsyncIterable<AdbPacketInit>;
46
+ getNextRequest: () => Promise<AdbPacketData>
47
+ ): AsyncIterable<AdbPacketData>;
48
48
  }
49
49
 
50
50
  export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
51
51
  credentialStore: AdbCredentialStore,
52
- getNextRequest: () => Promise<AdbPacket>,
53
- ): AsyncIterable<AdbPacketInit> {
52
+ getNextRequest: () => Promise<AdbPacketData>,
53
+ ): AsyncIterable<AdbPacketData> {
54
54
  for await (const key of credentialStore.iterateKeys()) {
55
55
  const packet = await getNextRequest();
56
56
 
@@ -58,32 +58,33 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
58
58
  return;
59
59
  }
60
60
 
61
- const signature = sign(key, packet.payload!);
61
+ const signature = sign(key, packet.payload);
62
62
  yield {
63
63
  command: AdbCommand.Auth,
64
64
  arg0: AdbAuthType.Signature,
65
65
  arg1: 0,
66
- payload: signature,
66
+ payload: new Uint8Array(signature),
67
67
  };
68
68
  }
69
69
  };
70
70
 
71
71
  export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
72
72
  credentialStore: AdbCredentialStore,
73
- getNextRequest: () => Promise<AdbPacket>,
74
- ): AsyncIterable<AdbPacketInit> {
73
+ getNextRequest: () => Promise<AdbPacketData>,
74
+ ): AsyncIterable<AdbPacketData> {
75
75
  const packet = await getNextRequest();
76
76
 
77
77
  if (packet.arg0 !== AdbAuthType.Token) {
78
78
  return;
79
79
  }
80
80
 
81
- let privateKey: ArrayBuffer | undefined;
81
+ let privateKey: Uint8Array | undefined;
82
82
  for await (const key of credentialStore.iterateKeys()) {
83
83
  privateKey = key;
84
84
  break;
85
85
  }
86
86
 
87
+
87
88
  if (!privateKey) {
88
89
  privateKey = await credentialStore.generateKey();
89
90
  }
@@ -93,10 +94,13 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
93
94
 
94
95
  // The public key is null terminated,
95
96
  // So we allocate the buffer with one extra byte.
96
- const publicKeyBuffer = new ArrayBuffer(publicKeyBase64Length + 1);
97
+ const publicKeyBuffer = new Uint8Array(publicKeyBase64Length + 1);
97
98
 
98
99
  calculatePublicKey(privateKey, publicKeyBuffer);
99
- encodeBase64(publicKeyBuffer, 0, publicKeyLength, publicKeyBuffer);
100
+ encodeBase64(
101
+ publicKeyBuffer.subarray(0, publicKeyLength),
102
+ publicKeyBuffer
103
+ );
100
104
 
101
105
  yield {
102
106
  command: AdbCommand.Auth,
@@ -106,19 +110,19 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
106
110
  };
107
111
  };
108
112
 
109
- export const AdbDefaultAuthenticators: AdbAuthenticator[] = [
113
+ export const ADB_DEFAULT_AUTHENTICATORS: AdbAuthenticator[] = [
110
114
  AdbSignatureAuthenticator,
111
115
  AdbPublicKeyAuthenticator,
112
116
  ];
113
117
 
114
- export class AdbAuthenticationHandler implements Disposable {
118
+ export class AdbAuthenticationProcessor implements Disposable {
115
119
  public readonly authenticators: readonly AdbAuthenticator[];
116
120
 
117
121
  private readonly credentialStore: AdbCredentialStore;
118
122
 
119
- private pendingRequest = new PromiseResolver<AdbPacket>();
123
+ private pendingRequest = new PromiseResolver<AdbPacketData>();
120
124
 
121
- private iterator: AsyncIterator<AdbPacketInit> | undefined;
125
+ private iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
122
126
 
123
127
  public constructor(
124
128
  authenticators: readonly AdbAuthenticator[],
@@ -128,16 +132,16 @@ export class AdbAuthenticationHandler implements Disposable {
128
132
  this.credentialStore = credentialStore;
129
133
  }
130
134
 
131
- private getNextRequest = (): Promise<AdbPacket> => {
135
+ private getNextRequest = (): Promise<AdbPacketData> => {
132
136
  return this.pendingRequest.promise;
133
137
  };
134
138
 
135
- private async* runAuthenticator(): AsyncGenerator<AdbPacketInit> {
139
+ private async* invokeAuthenticator(): AsyncGenerator<AdbPacketData, void, void> {
136
140
  for (const authenticator of this.authenticators) {
137
141
  for await (const packet of authenticator(this.credentialStore, this.getNextRequest)) {
138
142
  // If the authenticator yielded a response
139
143
  // Prepare `nextRequest` for next authentication request
140
- this.pendingRequest = new PromiseResolver<AdbPacket>();
144
+ this.pendingRequest = new PromiseResolver();
141
145
 
142
146
  // Yield the response to outer layer
143
147
  yield packet;
@@ -146,17 +150,20 @@ export class AdbAuthenticationHandler implements Disposable {
146
150
  // If the authenticator returned,
147
151
  // Next authenticator will be given the same `pendingRequest`
148
152
  }
149
-
150
- throw new Error('Cannot authenticate with device');
151
153
  }
152
154
 
153
- public async handle(packet: AdbPacket): Promise<AdbPacketInit> {
155
+ public async process(packet: AdbPacketData): Promise<AdbPacketData> {
154
156
  if (!this.iterator) {
155
- this.iterator = this.runAuthenticator();
157
+ this.iterator = this.invokeAuthenticator();
156
158
  }
157
159
 
158
160
  this.pendingRequest.resolve(packet);
161
+
159
162
  const result = await this.iterator.next();
163
+ if (result.done) {
164
+ throw new Error('No authenticator can handle the request');
165
+ }
166
+
160
167
  return result.value;
161
168
  }
162
169
 
package/src/backend.ts CHANGED
@@ -1,20 +1,11 @@
1
- import type { Event } from '@yume-chan/event';
2
1
  import type { ValueOrPromise } from '@yume-chan/struct';
2
+ import type { AdbPacketData, AdbPacketInit } from "./packet.js";
3
+ import type { ReadableWritablePair } from "./stream/index.js";
3
4
 
4
5
  export interface AdbBackend {
5
6
  readonly serial: string;
6
7
 
7
8
  readonly name: string | undefined;
8
9
 
9
- readonly connected: boolean;
10
-
11
- readonly onDisconnected: Event<void>;
12
-
13
- connect?(): ValueOrPromise<void>;
14
-
15
- read(length: number): ValueOrPromise<ArrayBuffer>;
16
-
17
- write(buffer: ArrayBuffer): ValueOrPromise<void>;
18
-
19
- dispose(): ValueOrPromise<void>;
10
+ connect(): ValueOrPromise<ReadableWritablePair<AdbPacketData, AdbPacketInit>>;
20
11
  }
@@ -1,5 +1,5 @@
1
1
  import { AutoDisposable } from '@yume-chan/event';
2
- import { Adb } from '../adb';
2
+ import type { Adb } from '../adb.js';
3
3
 
4
4
  export class AdbCommandBase extends AutoDisposable {
5
5
  protected adb: Adb;
@@ -1,6 +1,6 @@
1
1
  import Struct from "@yume-chan/struct";
2
- import { Adb } from '../adb';
3
- import { AdbBufferedStream } from '../stream';
2
+ import type { Adb } from '../adb.js';
3
+ import { AdbBufferedStream } from '../stream/index.js';
4
4
 
5
5
  const Version =
6
6
  new Struct({ littleEndian: true })
@@ -33,7 +33,7 @@ export const AdbFrameBufferV1 =
33
33
  .uint32('green_length')
34
34
  .uint32('alpha_offset')
35
35
  .uint32('alpha_length')
36
- .uint8ClampedArray('data', { lengthField: 'size' });
36
+ .uint8Array('data', { lengthField: 'size' });
37
37
 
38
38
  export type AdbFrameBufferV1 = typeof AdbFrameBufferV1['TDeserializeResult'];
39
39
 
@@ -52,7 +52,7 @@ export const AdbFrameBufferV2 =
52
52
  .uint32('green_length')
53
53
  .uint32('alpha_offset')
54
54
  .uint32('alpha_length')
55
- .uint8ClampedArray('data', { lengthField: 'size' });
55
+ .uint8Array('data', { lengthField: 'size' });
56
56
 
57
57
  export type AdbFrameBufferV2 = typeof AdbFrameBufferV2['TDeserializeResult'];
58
58
 
@@ -1,8 +1,8 @@
1
- export * from './base';
2
- export * from './demo-mode';
3
- export * from './framebuffer';
4
- export * from './install';
5
- export * from './reverse';
6
- export * from './shell';
7
- export * from './sync';
8
- export * from './tcpip';
1
+ export * from './base.js';
2
+ export * from './framebuffer.js';
3
+ export * from './install.js';
4
+ export * from './power.js';
5
+ export * from './reverse.js';
6
+ export * from './subprocess/index.js';
7
+ export * from './sync/index.js';
8
+ export * from './tcpip.js';