@yume-chan/adb 0.0.9 → 0.0.10

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 (205) hide show
  1. package/CHANGELOG.json +23 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +21 -21
  4. package/README.md +227 -257
  5. package/dts/adb.d.ts.map +1 -1
  6. package/dts/auth.d.ts +11 -1
  7. package/dts/auth.d.ts.map +1 -1
  8. package/dts/backend.d.ts +3 -5
  9. package/dts/backend.d.ts.map +1 -1
  10. package/dts/commands/demo-mode.d.ts +3 -3
  11. package/dts/commands/demo-mode.d.ts.map +1 -1
  12. package/dts/commands/reverse.d.ts +2 -2
  13. package/dts/commands/reverse.d.ts.map +1 -1
  14. package/dts/commands/shell/index.d.ts +31 -5
  15. package/dts/commands/shell/index.d.ts.map +1 -1
  16. package/dts/commands/shell/legacy.d.ts +13 -2
  17. package/dts/commands/shell/legacy.d.ts.map +1 -1
  18. package/dts/commands/shell/protocol.d.ts +11 -3
  19. package/dts/commands/shell/protocol.d.ts.map +1 -1
  20. package/dts/commands/shell/types.d.ts +35 -3
  21. package/dts/commands/shell/types.d.ts.map +1 -1
  22. package/dts/commands/sync/request.d.ts +1 -1
  23. package/dts/commands/sync/request.d.ts.map +1 -1
  24. package/dts/commands/sync/response.d.ts +2 -2
  25. package/dts/commands/sync/response.d.ts.map +1 -1
  26. package/dts/commands/sync/sync.d.ts.map +1 -1
  27. package/dts/crypto.d.ts +7 -0
  28. package/dts/crypto.d.ts.map +1 -1
  29. package/dts/packet.d.ts.map +1 -1
  30. package/dts/socket/close-event-emitter.d.ts.map +1 -1
  31. package/dts/socket/controller.d.ts.map +1 -1
  32. package/dts/socket/data-event-emitter.d.ts.map +1 -1
  33. package/dts/socket/dispatcher.d.ts.map +1 -1
  34. package/dts/stream/buffered-stream.d.ts +2 -4
  35. package/dts/stream/buffered-stream.d.ts.map +1 -1
  36. package/dts/utils/encoding.d.ts +3 -0
  37. package/dts/utils/encoding.d.ts.map +1 -0
  38. package/dts/utils/index.d.ts +1 -0
  39. package/dts/utils/index.d.ts.map +1 -1
  40. package/esm/adb.js +3 -2
  41. package/esm/adb.js.map +1 -1
  42. package/esm/auth.js +2 -2
  43. package/esm/auth.js.map +1 -1
  44. package/esm/commands/demo-mode.js +9 -9
  45. package/esm/commands/demo-mode.js.map +1 -1
  46. package/esm/commands/reverse.js +3 -2
  47. package/esm/commands/reverse.js.map +1 -1
  48. package/esm/commands/shell/index.js +28 -18
  49. package/esm/commands/shell/index.js.map +1 -1
  50. package/esm/commands/shell/legacy.js +16 -0
  51. package/esm/commands/shell/legacy.js.map +1 -1
  52. package/esm/commands/shell/protocol.js +36 -9
  53. package/esm/commands/shell/protocol.js.map +1 -1
  54. package/esm/commands/sync/request.js +5 -4
  55. package/esm/commands/sync/request.js.map +1 -1
  56. package/esm/commands/sync/response.js +4 -3
  57. package/esm/commands/sync/response.js.map +1 -1
  58. package/esm/commands/sync/sync.js.map +1 -1
  59. package/esm/crypto.js +7 -2
  60. package/esm/crypto.js.map +1 -1
  61. package/esm/packet.js +12 -14
  62. package/esm/packet.js.map +1 -1
  63. package/esm/socket/close-event-emitter.js.map +1 -1
  64. package/esm/socket/controller.js.map +1 -1
  65. package/esm/socket/data-event-emitter.js.map +1 -1
  66. package/esm/socket/dispatcher.js +7 -7
  67. package/esm/socket/dispatcher.js.map +1 -1
  68. package/esm/stream/buffered-stream.js +0 -6
  69. package/esm/stream/buffered-stream.js.map +1 -1
  70. package/esm/utils/encoding.js +11 -0
  71. package/esm/utils/encoding.js.map +1 -0
  72. package/esm/utils/index.js +1 -0
  73. package/esm/utils/index.js.map +1 -1
  74. package/package.json +44 -51
  75. package/src/adb.ts +243 -242
  76. package/src/auth.ts +166 -156
  77. package/src/backend.ts +20 -24
  78. package/src/commands/base.ts +11 -11
  79. package/src/commands/demo-mode.ts +160 -160
  80. package/src/commands/framebuffer.ts +73 -73
  81. package/src/commands/index.ts +8 -8
  82. package/src/commands/install.ts +21 -21
  83. package/src/commands/reverse.ts +134 -133
  84. package/src/commands/shell/index.ts +87 -68
  85. package/src/commands/shell/legacy.ts +59 -42
  86. package/src/commands/shell/protocol.ts +121 -94
  87. package/src/commands/shell/types.ts +55 -21
  88. package/src/commands/shell/utils.ts +20 -20
  89. package/src/commands/sync/index.ts +7 -7
  90. package/src/commands/sync/list.ts +39 -39
  91. package/src/commands/sync/pull.ts +34 -34
  92. package/src/commands/sync/push.ts +46 -46
  93. package/src/commands/sync/request.ts +49 -48
  94. package/src/commands/sync/response.ts +58 -57
  95. package/src/commands/sync/stat.ts +121 -121
  96. package/src/commands/sync/sync.ts +113 -113
  97. package/src/commands/tcpip.ts +21 -21
  98. package/src/crypto.ts +285 -280
  99. package/src/features.ts +5 -5
  100. package/src/index.ts +10 -10
  101. package/src/packet.ts +84 -89
  102. package/src/socket/close-event-emitter.ts +28 -28
  103. package/src/socket/controller.ts +105 -105
  104. package/src/socket/data-event-emitter.ts +27 -27
  105. package/src/socket/dispatcher.ts +269 -269
  106. package/src/socket/index.ts +6 -6
  107. package/src/socket/logger.ts +12 -12
  108. package/src/socket/socket.ts +28 -28
  109. package/src/stream/buffered-stream.ts +92 -100
  110. package/src/stream/index.ts +2 -2
  111. package/src/stream/stream.ts +50 -50
  112. package/src/utils/auto-reset-event.ts +41 -41
  113. package/src/utils/base64.ts +274 -274
  114. package/src/utils/chunk.ts +70 -70
  115. package/src/utils/encoding.ts +13 -0
  116. package/src/utils/event-queue.ts +145 -145
  117. package/src/utils/index.ts +5 -4
  118. package/cjs/adb.js +0 -205
  119. package/cjs/adb.js.map +0 -1
  120. package/cjs/auth.js +0 -141
  121. package/cjs/auth.js.map +0 -1
  122. package/cjs/backend.js +0 -3
  123. package/cjs/backend.js.map +0 -1
  124. package/cjs/commands/base.js +0 -12
  125. package/cjs/commands/base.js.map +0 -1
  126. package/cjs/commands/demo-mode.js +0 -174
  127. package/cjs/commands/demo-mode.js.map +0 -1
  128. package/cjs/commands/framebuffer.js +0 -66
  129. package/cjs/commands/framebuffer.js.map +0 -1
  130. package/cjs/commands/index.js +0 -12
  131. package/cjs/commands/index.js.map +0 -1
  132. package/cjs/commands/install.js +0 -20
  133. package/cjs/commands/install.js.map +0 -1
  134. package/cjs/commands/reverse.js +0 -105
  135. package/cjs/commands/reverse.js.map +0 -1
  136. package/cjs/commands/shell/index.js +0 -56
  137. package/cjs/commands/shell/index.js.map +0 -1
  138. package/cjs/commands/shell/legacy.js +0 -37
  139. package/cjs/commands/shell/legacy.js.map +0 -1
  140. package/cjs/commands/shell/protocol.js +0 -83
  141. package/cjs/commands/shell/protocol.js.map +0 -1
  142. package/cjs/commands/shell/types.js +0 -3
  143. package/cjs/commands/shell/types.js.map +0 -1
  144. package/cjs/commands/shell/utils.js +0 -23
  145. package/cjs/commands/shell/utils.js.map +0 -1
  146. package/cjs/commands/sync/index.js +0 -11
  147. package/cjs/commands/sync/index.js.map +0 -1
  148. package/cjs/commands/sync/list.js +0 -36
  149. package/cjs/commands/sync/list.js.map +0 -1
  150. package/cjs/commands/sync/pull.js +0 -34
  151. package/cjs/commands/sync/pull.js.map +0 -1
  152. package/cjs/commands/sync/push.js +0 -49
  153. package/cjs/commands/sync/push.js.map +0 -1
  154. package/cjs/commands/sync/request.js +0 -48
  155. package/cjs/commands/sync/request.js.map +0 -1
  156. package/cjs/commands/sync/response.js +0 -53
  157. package/cjs/commands/sync/response.js.map +0 -1
  158. package/cjs/commands/sync/stat.js +0 -109
  159. package/cjs/commands/sync/stat.js.map +0 -1
  160. package/cjs/commands/sync/sync.js +0 -118
  161. package/cjs/commands/sync/sync.js.map +0 -1
  162. package/cjs/commands/tcpip.js +0 -28
  163. package/cjs/commands/tcpip.js.map +0 -1
  164. package/cjs/crypto.js +0 -231
  165. package/cjs/crypto.js.map +0 -1
  166. package/cjs/features.js +0 -10
  167. package/cjs/features.js.map +0 -1
  168. package/cjs/index.js +0 -14
  169. package/cjs/index.js.map +0 -1
  170. package/cjs/packet.js +0 -77
  171. package/cjs/packet.js.map +0 -1
  172. package/cjs/socket/close-event-emitter.js +0 -32
  173. package/cjs/socket/close-event-emitter.js.map +0 -1
  174. package/cjs/socket/controller.js +0 -84
  175. package/cjs/socket/controller.js.map +0 -1
  176. package/cjs/socket/data-event-emitter.js +0 -29
  177. package/cjs/socket/data-event-emitter.js.map +0 -1
  178. package/cjs/socket/dispatcher.js +0 -219
  179. package/cjs/socket/dispatcher.js.map +0 -1
  180. package/cjs/socket/index.js +0 -10
  181. package/cjs/socket/index.js.map +0 -1
  182. package/cjs/socket/logger.js +0 -3
  183. package/cjs/socket/logger.js.map +0 -1
  184. package/cjs/socket/socket.js +0 -24
  185. package/cjs/socket/socket.js.map +0 -1
  186. package/cjs/stream/buffered-stream.js +0 -78
  187. package/cjs/stream/buffered-stream.js.map +0 -1
  188. package/cjs/stream/index.js +0 -6
  189. package/cjs/stream/index.js.map +0 -1
  190. package/cjs/stream/stream.js +0 -45
  191. package/cjs/stream/stream.js.map +0 -1
  192. package/cjs/utils/auto-reset-event.js +0 -37
  193. package/cjs/utils/auto-reset-event.js.map +0 -1
  194. package/cjs/utils/base64.js +0 -210
  195. package/cjs/utils/base64.js.map +0 -1
  196. package/cjs/utils/chunk.js +0 -75
  197. package/cjs/utils/chunk.js.map +0 -1
  198. package/cjs/utils/event-queue.js +0 -108
  199. package/cjs/utils/event-queue.js.map +0 -1
  200. package/cjs/utils/index.js +0 -8
  201. package/cjs/utils/index.js.map +0 -1
  202. package/tsconfig.cjs.json +0 -11
  203. package/tsconfig.esm.json +0 -28
  204. package/tsconfig.esm.tsbuildinfo +0 -1305
  205. package/tsconfig.json +0 -18
package/package.json CHANGED
@@ -1,51 +1,44 @@
1
- {
2
- "name": "@yume-chan/adb",
3
- "version": "0.0.9",
4
- "description": "TypeScript implementation of Android Debug Bridge (ADB) protocol.",
5
- "keywords": [
6
- "adb",
7
- "android-phone"
8
- ],
9
- "license": "MIT",
10
- "author": {
11
- "name": "Simon Chan",
12
- "email": "cnsimonchan@live.com",
13
- "url": "https://chensi.moe/blog"
14
- },
15
- "homepage": "https://github.com/yume-chan/ya-webadb/tree/master/packages/adb#readme",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/yume-chan/ya-webadb.git",
19
- "directory": "packages/adb"
20
- },
21
- "bugs": {
22
- "url": "https://github.com/yume-chan/ya-webadb/issues"
23
- },
24
- "main": "cjs/index.js",
25
- "module": "esm/index.js",
26
- "types": "dts/index.d.ts",
27
- "scripts": {
28
- "build": "rimraf {cjs,esm,dts,*.tsbuildinfo} && tsc -b tsconfig.esm.json tsconfig.cjs.json",
29
- "build:watch": "tsc -b -w tsconfig.esm.json tsconfig.cjs.json",
30
- "test": "jest",
31
- "coverage": "jest --coverage",
32
- "prepublishOnly": "npm run build"
33
- },
34
- "dependencies": {
35
- "@yume-chan/async": "2.1.4",
36
- "@yume-chan/event": "^0.0.9",
37
- "@yume-chan/struct": "^0.0.9",
38
- "tslib": "2.1.0"
39
- },
40
- "devDependencies": {
41
- "@types/jest": "26.0.19",
42
- "jest": "26.6.3",
43
- "rimraf": "3.0.2",
44
- "ts-jest": "26.4.4",
45
- "typescript": "4.2.3"
46
- },
47
- "publishConfig": {
48
- "access": "public"
49
- },
50
- "gitHead": "09c9764aa84fe0098342283a61758354ba2613b6"
51
- }
1
+ {
2
+ "name": "@yume-chan/adb",
3
+ "version": "0.0.10",
4
+ "description": "TypeScript implementation of Android Debug Bridge (ADB) protocol.",
5
+ "keywords": [
6
+ "adb",
7
+ "android-phone"
8
+ ],
9
+ "license": "MIT",
10
+ "author": {
11
+ "name": "Simon Chan",
12
+ "email": "cnsimonchan@live.com",
13
+ "url": "https://chensi.moe/blog"
14
+ },
15
+ "homepage": "https://github.com/yume-chan/ya-webadb/tree/master/libraries/adb#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/yume-chan/ya-webadb.git",
19
+ "directory": "libraries/adb"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/yume-chan/ya-webadb/issues"
23
+ },
24
+ "type": "module",
25
+ "module": "esm/index.js",
26
+ "types": "dts/index.d.ts",
27
+ "scripts": {
28
+ "build": "build-ts-package",
29
+ "build:watch": "build-ts-package --incremental",
30
+ "test": "jest --coverage",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "dependencies": {
34
+ "@yume-chan/async": "^2.1.4",
35
+ "@yume-chan/event": "^0.0.10",
36
+ "@yume-chan/struct": "^0.0.10",
37
+ "tslib": "^2.3.1"
38
+ },
39
+ "devDependencies": {
40
+ "jest": "^26.6.3",
41
+ "typescript": "^4.5.4",
42
+ "@yume-chan/ts-package-builder": "^1.0.0"
43
+ }
44
+ }
package/src/adb.ts CHANGED
@@ -1,242 +1,243 @@
1
- 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
-
10
- export enum AdbPropKey {
11
- Product = 'ro.product.name',
12
- Model = 'ro.product.model',
13
- Device = 'ro.product.device',
14
- Features = 'features',
15
- }
16
-
17
- export class Adb {
18
- private readonly _backend: AdbBackend;
19
-
20
- public get backend(): AdbBackend { return this._backend; }
21
-
22
- private readonly packetDispatcher: AdbPacketDispatcher;
23
-
24
- public get onDisconnected() { return this.backend.onDisconnected; }
25
-
26
- private _connected = false;
27
- public get connected() { return this._connected; }
28
-
29
- public get name() { return this.backend.name; }
30
-
31
- private _protocolVersion: number | undefined;
32
- public get protocolVersion() { return this._protocolVersion; }
33
-
34
- private _product: string | undefined;
35
- public get product() { return this._product; }
36
-
37
- private _model: string | undefined;
38
- public get model() { return this._model; }
39
-
40
- private _device: string | undefined;
41
- public get device() { return this._device; }
42
-
43
- private _features: AdbFeatures[] | undefined;
44
- public get features() { return this._features; }
45
-
46
- public readonly tcpip: AdbTcpIpCommand;
47
-
48
- public readonly reverse: AdbReverseCommand;
49
-
50
- public readonly demoMode: AdbDemoMode;
51
-
52
- public readonly childProcess: AdbChildProcess;
53
-
54
- public constructor(backend: AdbBackend, logger?: AdbLogger) {
55
- this._backend = backend;
56
- this.packetDispatcher = new AdbPacketDispatcher(backend, logger);
57
-
58
- this.tcpip = new AdbTcpIpCommand(this);
59
- this.reverse = new AdbReverseCommand(this.packetDispatcher);
60
- this.demoMode = new AdbDemoMode(this);
61
- this.childProcess = new AdbChildProcess(this);
62
-
63
- backend.onDisconnected(this.dispose, this);
64
- }
65
-
66
- public async connect(
67
- credentialStore: AdbCredentialStore,
68
- authenticators = AdbDefaultAuthenticators
69
- ): Promise<void> {
70
- await this.backend.connect?.();
71
- this.packetDispatcher.maxPayloadSize = 0x1000;
72
- this.packetDispatcher.calculateChecksum = true;
73
- this.packetDispatcher.appendNullToServiceString = true;
74
- this.packetDispatcher.start();
75
-
76
- const version = 0x01000001;
77
- const versionNoChecksum = 0x01000001;
78
- const maxPayloadSize = 0x100000;
79
-
80
- const features = [
81
- 'shell_v2',
82
- 'cmd',
83
- AdbFeatures.StatV2,
84
- 'ls_v2',
85
- 'fixed_push_mkdir',
86
- 'apex',
87
- 'abb',
88
- 'fixed_push_symlink_timestamp',
89
- 'abb_exec',
90
- 'remount_shell',
91
- 'track_app',
92
- 'sendrecv_v2',
93
- 'sendrecv_v2_brotli',
94
- 'sendrecv_v2_lz4',
95
- 'sendrecv_v2_zstd',
96
- 'sendrecv_v2_dry_run_send',
97
- ].join(',');
98
-
99
- const resolver = new PromiseResolver<void>();
100
- const authHandler = new AdbAuthenticationHandler(authenticators, credentialStore);
101
- const disposableList = new DisposableList();
102
- disposableList.add(this.packetDispatcher.onPacket(async (e) => {
103
- e.handled = true;
104
-
105
- const { packet } = e;
106
- try {
107
- switch (packet.command) {
108
- case AdbCommand.Connect:
109
- this.packetDispatcher.maxPayloadSize = Math.min(maxPayloadSize, packet.arg1);
110
-
111
- const finalVersion = Math.min(version, packet.arg0);
112
- this._protocolVersion = finalVersion;
113
-
114
- if (finalVersion >= versionNoChecksum) {
115
- this.packetDispatcher.calculateChecksum = false;
116
- // Android prior to 9.0.0 uses char* to parse service string
117
- // thus requires an extra null character
118
- this.packetDispatcher.appendNullToServiceString = false;
119
- }
120
-
121
- this.parseBanner(this.backend.decodeUtf8(packet.payload!));
122
- resolver.resolve();
123
- break;
124
- case AdbCommand.Auth:
125
- const authPacket = await authHandler.handle(e.packet);
126
- await this.packetDispatcher.sendPacket(authPacket);
127
- break;
128
- case AdbCommand.Close:
129
- // Last connection was interrupted
130
- // Ignore this packet, device will recover
131
- break;
132
- default:
133
- throw new Error('Device not in correct state. Reconnect your device and try again');
134
- }
135
- } catch (e) {
136
- resolver.reject(e);
137
- }
138
- }));
139
-
140
- disposableList.add(this.packetDispatcher.onError(e => {
141
- resolver.reject(e);
142
- }));
143
-
144
- // Android prior 9.0.0 requires the null character
145
- // Newer versions can also handle the null character
146
- // The terminating `;` is required in formal definition
147
- // But ADB daemon can also work without it
148
- await this.packetDispatcher.sendPacket(
149
- AdbCommand.Connect,
150
- version,
151
- maxPayloadSize,
152
- `host::features=${features};\0`
153
- );
154
-
155
- try {
156
- await resolver.promise;
157
- this._connected = true;
158
- } finally {
159
- disposableList.dispose();
160
- }
161
- }
162
-
163
- private parseBanner(banner: string): void {
164
- this._features = [];
165
-
166
- const pieces = banner.split('::');
167
- if (pieces.length > 1) {
168
- const props = pieces[1];
169
- for (const prop of props.split(';')) {
170
- if (!prop) {
171
- continue;
172
- }
173
-
174
- const keyValue = prop.split('=');
175
- if (keyValue.length !== 2) {
176
- continue;
177
- }
178
-
179
- const [key, value] = keyValue;
180
- switch (key) {
181
- case AdbPropKey.Product:
182
- this._product = value;
183
- break;
184
- case AdbPropKey.Model:
185
- this._model = value;
186
- break;
187
- case AdbPropKey.Device:
188
- this._device = value;
189
- break;
190
- case AdbPropKey.Features:
191
- this._features = value.split(',') as AdbFeatures[];
192
- break;
193
- }
194
- }
195
- }
196
- }
197
-
198
- public async getProp(key: string): Promise<string> {
199
- const output = await this.childProcess.exec('getprop', key);
200
- return output.trim();
201
- }
202
-
203
- public async rm(...filenames: string[]): Promise<string> {
204
- return await this.childProcess.exec('rm', '-rf', ...filenames.map(arg => escapeArg(arg)));
205
- }
206
-
207
- public async install(
208
- apk: ArrayLike<number> | ArrayBufferLike | AsyncIterable<ArrayBuffer>,
209
- onProgress?: (uploaded: number) => void,
210
- ): Promise<void> {
211
- return await install(this, apk, onProgress);
212
- }
213
-
214
- public async sync(): Promise<AdbSync> {
215
- const socket = await this.createSocket('sync:');
216
- return new AdbSync(this, socket);
217
- }
218
-
219
- public async framebuffer(): Promise<AdbFrameBuffer> {
220
- return framebuffer(this);
221
- }
222
-
223
- public async createSocket(service: string): Promise<AdbSocket> {
224
- return this.packetDispatcher.createSocket(service);
225
- }
226
-
227
- public async createSocketAndReadAll(service: string): Promise<string> {
228
- const socket = await this.createSocket(service);
229
- const resolver = new PromiseResolver<string>();
230
- let result = '';
231
- socket.onData(buffer => {
232
- result += this.backend.decodeUtf8(buffer);
233
- });
234
- socket.onClose(() => resolver.resolve(result));
235
- return resolver.promise;
236
- }
237
-
238
- public async dispose(): Promise<void> {
239
- this.packetDispatcher.dispose();
240
- await this.backend.dispose();
241
- }
242
- }
1
+ 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";
10
+
11
+ export enum AdbPropKey {
12
+ Product = 'ro.product.name',
13
+ Model = 'ro.product.model',
14
+ Device = 'ro.product.device',
15
+ Features = 'features',
16
+ }
17
+
18
+ export class Adb {
19
+ private readonly _backend: AdbBackend;
20
+
21
+ public get backend(): AdbBackend { return this._backend; }
22
+
23
+ private readonly packetDispatcher: AdbPacketDispatcher;
24
+
25
+ public get onDisconnected() { return this.backend.onDisconnected; }
26
+
27
+ private _connected = false;
28
+ public get connected() { return this._connected; }
29
+
30
+ public get name() { return this.backend.name; }
31
+
32
+ private _protocolVersion: number | undefined;
33
+ public get protocolVersion() { return this._protocolVersion; }
34
+
35
+ private _product: string | undefined;
36
+ public get product() { return this._product; }
37
+
38
+ private _model: string | undefined;
39
+ public get model() { return this._model; }
40
+
41
+ private _device: string | undefined;
42
+ public get device() { return this._device; }
43
+
44
+ private _features: AdbFeatures[] | undefined;
45
+ public get features() { return this._features; }
46
+
47
+ public readonly tcpip: AdbTcpIpCommand;
48
+
49
+ public readonly reverse: AdbReverseCommand;
50
+
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
+ }
66
+
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);
138
+ }
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
+ );
155
+
156
+ try {
157
+ await resolver.promise;
158
+ this._connected = true;
159
+ } finally {
160
+ disposableList.dispose();
161
+ }
162
+ }
163
+
164
+ private parseBanner(banner: string): void {
165
+ this._features = [];
166
+
167
+ const pieces = banner.split('::');
168
+ if (pieces.length > 1) {
169
+ const props = pieces[1];
170
+ for (const prop of props.split(';')) {
171
+ if (!prop) {
172
+ continue;
173
+ }
174
+
175
+ const keyValue = prop.split('=');
176
+ if (keyValue.length !== 2) {
177
+ continue;
178
+ }
179
+
180
+ const [key, value] = keyValue;
181
+ switch (key) {
182
+ case AdbPropKey.Product:
183
+ this._product = value;
184
+ break;
185
+ case AdbPropKey.Model:
186
+ this._model = value;
187
+ break;
188
+ case AdbPropKey.Device:
189
+ this._device = value;
190
+ break;
191
+ case AdbPropKey.Features:
192
+ this._features = value.split(',') as AdbFeatures[];
193
+ break;
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ public async getProp(key: string): Promise<string> {
200
+ const output = await this.childProcess.exec('getprop', key);
201
+ return output.trim();
202
+ }
203
+
204
+ public async rm(...filenames: string[]): Promise<string> {
205
+ return await this.childProcess.exec('rm', '-rf', ...filenames.map(arg => escapeArg(arg)));
206
+ }
207
+
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);
213
+ }
214
+
215
+ public async sync(): Promise<AdbSync> {
216
+ const socket = await this.createSocket('sync:');
217
+ return new AdbSync(this, socket);
218
+ }
219
+
220
+ public async framebuffer(): Promise<AdbFrameBuffer> {
221
+ return framebuffer(this);
222
+ }
223
+
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
+ public async dispose(): Promise<void> {
240
+ this.packetDispatcher.dispose();
241
+ await this.backend.dispose();
242
+ }
243
+ }