@yume-chan/adb 0.0.17 → 0.0.19

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 (177) hide show
  1. package/CHANGELOG.json +27 -0
  2. package/CHANGELOG.md +16 -1
  3. package/LICENSE +1 -1
  4. package/esm/adb.d.ts +63 -60
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +257 -229
  7. package/esm/adb.js.map +1 -1
  8. package/esm/auth.d.ts +51 -51
  9. package/esm/auth.d.ts.map +1 -1
  10. package/esm/auth.js +95 -96
  11. package/esm/auth.js.map +1 -1
  12. package/esm/backend.d.ts +8 -8
  13. package/esm/backend.d.ts.map +1 -1
  14. package/esm/backend.js +1 -1
  15. package/esm/commands/base.d.ts +6 -6
  16. package/esm/commands/base.js +8 -8
  17. package/esm/commands/framebuffer.d.ts +53 -53
  18. package/esm/commands/framebuffer.d.ts.map +1 -1
  19. package/esm/commands/framebuffer.js +46 -47
  20. package/esm/commands/framebuffer.js.map +1 -1
  21. package/esm/commands/index.d.ts +7 -8
  22. package/esm/commands/index.d.ts.map +1 -1
  23. package/esm/commands/index.js +7 -8
  24. package/esm/commands/index.js.map +1 -1
  25. package/esm/commands/install.d.ts +9 -3
  26. package/esm/commands/install.d.ts.map +1 -1
  27. package/esm/commands/install.js +27 -21
  28. package/esm/commands/install.js.map +1 -1
  29. package/esm/commands/power.d.ts +22 -22
  30. package/esm/commands/power.d.ts.map +1 -1
  31. package/esm/commands/power.js +48 -44
  32. package/esm/commands/power.js.map +1 -1
  33. package/esm/commands/reverse.d.ts +41 -28
  34. package/esm/commands/reverse.d.ts.map +1 -1
  35. package/esm/commands/reverse.js +125 -98
  36. package/esm/commands/reverse.js.map +1 -1
  37. package/esm/commands/subprocess/command.d.ts +57 -51
  38. package/esm/commands/subprocess/command.d.ts.map +1 -1
  39. package/esm/commands/subprocess/command.js +86 -82
  40. package/esm/commands/subprocess/command.js.map +1 -1
  41. package/esm/commands/subprocess/index.d.ts +3 -3
  42. package/esm/commands/subprocess/index.js +3 -3
  43. package/esm/commands/subprocess/protocols/index.d.ts +3 -3
  44. package/esm/commands/subprocess/protocols/index.js +3 -3
  45. package/esm/commands/subprocess/protocols/none.d.ts +35 -35
  46. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  47. package/esm/commands/subprocess/protocols/none.js +65 -55
  48. package/esm/commands/subprocess/protocols/none.js.map +1 -1
  49. package/esm/commands/subprocess/protocols/shell.d.ts +39 -38
  50. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  51. package/esm/commands/subprocess/protocols/shell.js +166 -154
  52. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  53. package/esm/commands/subprocess/protocols/types.d.ts +49 -49
  54. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
  55. package/esm/commands/subprocess/protocols/types.js +1 -1
  56. package/esm/commands/subprocess/utils.d.ts +1 -1
  57. package/esm/commands/subprocess/utils.js +18 -18
  58. package/esm/commands/sync/index.d.ts +8 -7
  59. package/esm/commands/sync/index.d.ts.map +1 -1
  60. package/esm/commands/sync/index.js +8 -7
  61. package/esm/commands/sync/index.js.map +1 -1
  62. package/esm/commands/sync/list.d.ts +42 -40
  63. package/esm/commands/sync/list.d.ts.map +1 -1
  64. package/esm/commands/sync/list.js +67 -35
  65. package/esm/commands/sync/list.js.map +1 -1
  66. package/esm/commands/sync/pull.d.ts +13 -11
  67. package/esm/commands/sync/pull.d.ts.map +1 -1
  68. package/esm/commands/sync/pull.js +35 -36
  69. package/esm/commands/sync/pull.js.map +1 -1
  70. package/esm/commands/sync/push.d.ts +46 -7
  71. package/esm/commands/sync/push.d.ts.map +1 -1
  72. package/esm/commands/sync/push.js +89 -23
  73. package/esm/commands/sync/push.js.map +1 -1
  74. package/esm/commands/sync/request.d.ts +26 -23
  75. package/esm/commands/sync/request.d.ts.map +1 -1
  76. package/esm/commands/sync/request.js +40 -42
  77. package/esm/commands/sync/request.js.map +1 -1
  78. package/esm/commands/sync/response.d.ts +19 -19
  79. package/esm/commands/sync/response.d.ts.map +1 -1
  80. package/esm/commands/sync/response.js +54 -54
  81. package/esm/commands/sync/response.js.map +1 -1
  82. package/esm/commands/sync/socket.d.ts +27 -0
  83. package/esm/commands/sync/socket.d.ts.map +1 -0
  84. package/esm/commands/sync/socket.js +66 -0
  85. package/esm/commands/sync/socket.js.map +1 -0
  86. package/esm/commands/sync/stat.d.ts +72 -72
  87. package/esm/commands/sync/stat.d.ts.map +1 -1
  88. package/esm/commands/sync/stat.js +116 -94
  89. package/esm/commands/sync/stat.js.map +1 -1
  90. package/esm/commands/sync/sync.d.ts +72 -60
  91. package/esm/commands/sync/sync.d.ts.map +1 -1
  92. package/esm/commands/sync/sync.js +127 -148
  93. package/esm/commands/sync/sync.js.map +1 -1
  94. package/esm/commands/tcpip.d.ts +5 -5
  95. package/esm/commands/tcpip.js +18 -18
  96. package/esm/crypto.d.ts +36 -36
  97. package/esm/crypto.d.ts.map +1 -1
  98. package/esm/crypto.js +248 -238
  99. package/esm/crypto.js.map +1 -1
  100. package/esm/features.d.ts +10 -7
  101. package/esm/features.d.ts.map +1 -1
  102. package/esm/features.js +13 -10
  103. package/esm/features.js.map +1 -1
  104. package/esm/index.d.ts +9 -9
  105. package/esm/index.d.ts.map +1 -1
  106. package/esm/index.js +9 -9
  107. package/esm/index.js.map +1 -1
  108. package/esm/packet.d.ts +45 -46
  109. package/esm/packet.d.ts.map +1 -1
  110. package/esm/packet.js +43 -46
  111. package/esm/packet.js.map +1 -1
  112. package/esm/socket/dispatcher.d.ts +59 -59
  113. package/esm/socket/dispatcher.d.ts.map +1 -1
  114. package/esm/socket/dispatcher.js +223 -231
  115. package/esm/socket/dispatcher.js.map +1 -1
  116. package/esm/socket/index.d.ts +2 -2
  117. package/esm/socket/index.js +2 -2
  118. package/esm/socket/socket.d.ts +59 -59
  119. package/esm/socket/socket.d.ts.map +1 -1
  120. package/esm/socket/socket.js +118 -98
  121. package/esm/socket/socket.js.map +1 -1
  122. package/esm/utils/auto-reset-event.d.ts +9 -9
  123. package/esm/utils/auto-reset-event.d.ts.map +1 -1
  124. package/esm/utils/auto-reset-event.js +33 -33
  125. package/esm/utils/auto-reset-event.js.map +1 -1
  126. package/esm/utils/base64.d.ts +27 -27
  127. package/esm/utils/base64.d.ts.map +1 -1
  128. package/esm/utils/base64.js +246 -245
  129. package/esm/utils/base64.js.map +1 -1
  130. package/esm/utils/conditional-variable.d.ts +9 -0
  131. package/esm/utils/conditional-variable.d.ts.map +1 -0
  132. package/esm/utils/conditional-variable.js +34 -0
  133. package/esm/utils/conditional-variable.js.map +1 -0
  134. package/esm/utils/index.d.ts +4 -3
  135. package/esm/utils/index.d.ts.map +1 -1
  136. package/esm/utils/index.js +4 -3
  137. package/esm/utils/index.js.map +1 -1
  138. package/package.json +17 -13
  139. package/src/adb.ts +160 -101
  140. package/src/auth.ts +31 -22
  141. package/src/backend.ts +6 -4
  142. package/src/commands/base.ts +2 -2
  143. package/src/commands/framebuffer.ts +37 -41
  144. package/src/commands/index.ts +7 -8
  145. package/src/commands/power.ts +15 -11
  146. package/src/commands/reverse.ts +74 -30
  147. package/src/commands/subprocess/command.ts +34 -23
  148. package/src/commands/subprocess/index.ts +3 -3
  149. package/src/commands/subprocess/protocols/index.ts +3 -3
  150. package/src/commands/subprocess/protocols/none.ts +26 -11
  151. package/src/commands/subprocess/protocols/shell.ts +110 -56
  152. package/src/commands/subprocess/protocols/types.ts +10 -6
  153. package/src/commands/subprocess/utils.ts +1 -1
  154. package/src/commands/sync/index.ts +8 -7
  155. package/src/commands/sync/list.ts +74 -28
  156. package/src/commands/sync/pull.ts +49 -37
  157. package/src/commands/sync/push.ts +153 -32
  158. package/src/commands/sync/request.ts +37 -34
  159. package/src/commands/sync/response.ts +34 -27
  160. package/src/commands/sync/socket.ts +97 -0
  161. package/src/commands/sync/stat.ts +98 -70
  162. package/src/commands/sync/sync.ts +78 -97
  163. package/src/commands/tcpip.ts +5 -5
  164. package/src/crypto.ts +60 -32
  165. package/src/features.ts +9 -6
  166. package/src/index.ts +9 -10
  167. package/src/packet.ts +41 -46
  168. package/src/socket/dispatcher.ts +130 -108
  169. package/src/socket/index.ts +2 -2
  170. package/src/socket/socket.ts +84 -34
  171. package/src/utils/auto-reset-event.ts +16 -17
  172. package/src/utils/base64.ts +51 -41
  173. package/src/utils/conditional-variable.ts +45 -0
  174. package/src/utils/index.ts +4 -3
  175. package/tsconfig.build.json +1 -1
  176. package/tsconfig.build.tsbuildinfo +1 -1
  177. package/src/commands/install.ts +0 -32
@@ -1,15 +1,17 @@
1
- import { AutoDisposable } from '@yume-chan/event';
2
- import { BufferedReadableStream, ReadableStream, WrapReadableStream, WrapWritableStream, WritableStream, WritableStreamDefaultWriter } from '@yume-chan/stream-extra';
3
-
4
- import type { Adb } from '../../adb.js';
5
- import { AdbFeatures } from '../../features.js';
6
- import type { AdbSocket } from '../../socket/index.js';
7
- import { AutoResetEvent } from '../../utils/index.js';
8
- import { escapeArg } from '../subprocess/index.js';
9
- import { adbSyncOpenDir, type AdbSyncEntry } from './list.js';
10
- import { adbSyncPull } from './pull.js';
11
- import { adbSyncPush } from './push.js';
12
- import { adbSyncLstat, adbSyncStat } from './stat.js';
1
+ import { AutoDisposable } from "@yume-chan/event";
2
+ import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
3
+
4
+ import type { Adb } from "../../adb.js";
5
+ import { AdbFeature } from "../../features.js";
6
+ import type { AdbSocket } from "../../socket/index.js";
7
+ import { escapeArg } from "../subprocess/index.js";
8
+
9
+ import type { AdbSyncEntry } from "./list.js";
10
+ import { adbSyncOpenDir } from "./list.js";
11
+ import { adbSyncPull } from "./pull.js";
12
+ import { adbSyncPush } from "./push.js";
13
+ import { AdbSyncSocket } from "./socket.js";
14
+ import { adbSyncLstat, adbSyncStat } from "./stat.js";
13
15
 
14
16
  /**
15
17
  * A simplified `dirname` function that only handles absolute unix paths.
@@ -17,94 +19,95 @@ import { adbSyncLstat, adbSyncStat } from './stat.js';
17
19
  * @returns the directory name of the input path
18
20
  */
19
21
  export function dirname(path: string): string {
20
- const end = path.lastIndexOf('/');
22
+ const end = path.lastIndexOf("/");
21
23
  if (end === -1) {
22
24
  throw new Error(`Invalid path`);
23
25
  }
24
26
  if (end === 0) {
25
- return '/';
27
+ return "/";
26
28
  }
27
29
  return path.substring(0, end);
28
30
  }
29
31
 
32
+ export interface AdbSyncWriteOptions {
33
+ filename: string;
34
+ file: ReadableStream<Consumable<Uint8Array>>;
35
+ mode?: number;
36
+ mtime?: number;
37
+ dryRun?: boolean;
38
+ }
39
+
30
40
  export class AdbSync extends AutoDisposable {
31
- protected adb: Adb;
41
+ protected _adb: Adb;
42
+ protected _socket: AdbSyncSocket;
32
43
 
33
- protected stream: BufferedReadableStream;
34
- // Getting another writer on a locked WritableStream will throw.
35
- // We don't want this behavior on higher-level APIs.
36
- // So we acquire the writer early and use a blocking lock to guard it.
37
- protected writer: WritableStreamDefaultWriter<Uint8Array>;
38
- protected sendLock = this.addDisposable(new AutoResetEvent());
44
+ private _supportsStat: boolean;
45
+ private _supportsListV2: boolean;
46
+ private _fixedPushMkdir: boolean;
47
+ private _supportsSendReceiveV2: boolean;
48
+ private _needPushMkdirWorkaround: boolean;
39
49
 
40
50
  public get supportsStat(): boolean {
41
- return this.adb.features.includes(AdbFeatures.StatV2);
51
+ return this._supportsStat;
42
52
  }
43
53
 
44
- public get supportsList2(): boolean {
45
- return this.adb.features.includes(AdbFeatures.ListV2);
54
+ public get supportsListV2(): boolean {
55
+ return this._supportsListV2;
46
56
  }
47
57
 
48
58
  public get fixedPushMkdir(): boolean {
49
- return this.adb.features.includes(AdbFeatures.FixedPushMkdir);
59
+ return this._fixedPushMkdir;
60
+ }
61
+
62
+ public get supportsSendReceiveV2(): boolean {
63
+ return this._supportsSendReceiveV2;
50
64
  }
51
65
 
52
66
  public get needPushMkdirWorkaround(): boolean {
53
- // https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
54
- return this.adb.features.includes(AdbFeatures.ShellV2) && !this.fixedPushMkdir;
67
+ return this._needPushMkdirWorkaround;
55
68
  }
56
69
 
57
70
  public constructor(adb: Adb, socket: AdbSocket) {
58
71
  super();
59
72
 
60
- this.adb = adb;
61
- this.stream = new BufferedReadableStream(socket.readable);
62
- this.writer = socket.writable.getWriter();
73
+ this._adb = adb;
74
+ this._socket = new AdbSyncSocket(socket, adb.maxPayloadSize);
75
+
76
+ this._supportsStat = adb.supportsFeature(AdbFeature.StatV2);
77
+ this._supportsListV2 = adb.supportsFeature(AdbFeature.ListV2);
78
+ this._fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir);
79
+ this._supportsSendReceiveV2 = adb.supportsFeature(
80
+ AdbFeature.SendReceiveV2
81
+ );
82
+ // https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
83
+ this._needPushMkdirWorkaround =
84
+ this._adb.supportsFeature(AdbFeature.ShellV2) &&
85
+ !this.fixedPushMkdir;
63
86
  }
64
87
 
65
88
  public async lstat(path: string) {
66
- await this.sendLock.wait();
67
-
68
- try {
69
- return adbSyncLstat(this.stream, this.writer, path, this.supportsStat);
70
- } finally {
71
- this.sendLock.notify();
72
- }
89
+ return await adbSyncLstat(this._socket, path, this.supportsStat);
73
90
  }
74
91
 
75
92
  public async stat(path: string) {
76
93
  if (!this.supportsStat) {
77
- throw new Error('Not supported');
94
+ throw new Error("Not supported");
78
95
  }
79
96
 
80
- await this.sendLock.wait();
81
-
82
- try {
83
- return adbSyncStat(this.stream, this.writer, path);
84
- } finally {
85
- this.sendLock.notify();
86
- }
97
+ return await adbSyncStat(this._socket, path);
87
98
  }
88
99
 
89
100
  public async isDirectory(path: string): Promise<boolean> {
90
101
  try {
91
- await this.lstat(path + '/');
102
+ await this.lstat(path + "/");
92
103
  return true;
93
104
  } catch (e) {
94
105
  return false;
95
106
  }
96
107
  }
97
108
 
98
- public async *opendir(
99
- path: string
100
- ): AsyncGenerator<AdbSyncEntry, void, void> {
101
- await this.sendLock.wait();
102
-
103
- try {
104
- yield* adbSyncOpenDir(this.stream, this.writer, path, this.supportsList2);
105
- } finally {
106
- this.sendLock.notify();
107
- }
109
+ public opendir(path: string): AsyncGenerator<AdbSyncEntry, void, void> {
110
+ return adbSyncOpenDir(this._socket, path, this.supportsListV2);
108
111
  }
109
112
 
110
113
  public async readdir(path: string) {
@@ -122,61 +125,39 @@ export class AdbSync extends AutoDisposable {
122
125
  * @returns A `ReadableStream` that reads from the file.
123
126
  */
124
127
  public read(filename: string): ReadableStream<Uint8Array> {
125
- return new WrapReadableStream({
126
- start: async () => {
127
- await this.sendLock.wait();
128
- return adbSyncPull(this.stream, this.writer, filename);
129
- },
130
- close: async () => {
131
- this.sendLock.notify();
132
- },
133
- });
128
+ return adbSyncPull(this._socket, filename);
134
129
  }
135
130
 
136
131
  /**
137
132
  * Write (or overwrite) a file on device.
138
133
  *
139
134
  * @param filename The full path of the file on device to write.
135
+ * @param file The content to write.
140
136
  * @param mode The unix permissions of the file.
141
137
  * @param mtime The modified time of the file.
142
138
  * @returns A `WritableStream` that writes to the file.
143
139
  */
144
- public write(
145
- filename: string,
146
- mode?: number,
147
- mtime?: number,
148
- ): WritableStream<Uint8Array> {
149
- return new WrapWritableStream({
150
- start: async () => {
151
- await this.sendLock.wait();
152
-
153
- if (this.needPushMkdirWorkaround) {
154
- // It may fail if the path is already existed.
155
- // Ignore the result.
156
- // TODO: sync: test push mkdir workaround (need an Android 8 device)
157
- await this.adb.subprocess.spawnAndWait([
158
- 'mkdir',
159
- '-p',
160
- escapeArg(dirname(filename)),
161
- ]);
162
- }
163
-
164
- return adbSyncPush(
165
- this.stream,
166
- this.writer,
167
- filename,
168
- mode,
169
- mtime,
170
- );
171
- },
172
- close: async () => {
173
- this.sendLock.notify();
174
- }
140
+ public async write(options: AdbSyncWriteOptions) {
141
+ if (this.needPushMkdirWorkaround) {
142
+ // It may fail if the path is already existed.
143
+ // Ignore the result.
144
+ // TODO: sync: test push mkdir workaround (need an Android 8 device)
145
+ await this._adb.subprocess.spawnAndWait([
146
+ "mkdir",
147
+ "-p",
148
+ escapeArg(dirname(options.filename)),
149
+ ]);
150
+ }
151
+
152
+ await adbSyncPush({
153
+ v2: this.supportsSendReceiveV2,
154
+ socket: this._socket,
155
+ ...options,
175
156
  });
176
157
  }
177
158
 
178
159
  public override async dispose() {
179
160
  super.dispose();
180
- await this.writer.close();
161
+ await this._socket.close();
181
162
  }
182
163
  }
@@ -1,4 +1,4 @@
1
- import { AdbCommandBase } from './base.js';
1
+ import { AdbCommandBase } from "./base.js";
2
2
 
3
3
  export class AdbTcpIpCommand extends AdbCommandBase {
4
4
  public async setPort(port: number): Promise<void> {
@@ -8,14 +8,14 @@ export class AdbTcpIpCommand extends AdbCommandBase {
8
8
 
9
9
  const output = await this.adb.createSocketAndWait(`tcpip:${port}`);
10
10
  if (output !== `restarting in TCP mode port: ${port}\n`) {
11
- throw new Error('Invalid response');
11
+ throw new Error("Invalid response");
12
12
  }
13
13
  }
14
14
 
15
15
  public async disable(): Promise<void> {
16
- const output = await this.adb.createSocketAndWait('usb:');
17
- if (output !== 'restarting in USB mode\n') {
18
- throw new Error('Invalid response');
16
+ const output = await this.adb.createSocketAndWait("usb:");
17
+ if (output !== "restarting in USB mode\n") {
18
+ throw new Error("Invalid response");
19
19
  }
20
20
  }
21
21
  }
package/src/crypto.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { getBigUint64, setBigUint64 } from '@yume-chan/dataview-bigint-polyfill/esm/fallback.js';
1
+ import {
2
+ getBigUint64,
3
+ setBigUint64,
4
+ } from "@yume-chan/dataview-bigint-polyfill/esm/fallback.js";
2
5
 
3
6
  const BigInt0 = BigInt(0);
4
7
  const BigInt1 = BigInt(1);
@@ -12,7 +15,11 @@ const BigInt64 = BigInt(64);
12
15
  * Only supports Big-Endian, because that's what ADB uses.
13
16
  * @param byteOffset The place in the buffer at which the value should be retrieved.
14
17
  */
15
- export function getBigUint(dataView: DataView, byteOffset: number, length: number): bigint {
18
+ export function getBigUint(
19
+ dataView: DataView,
20
+ byteOffset: number,
21
+ length: number
22
+ ): bigint {
16
23
  let result = BigInt0;
17
24
 
18
25
  // Currently `length` must be a multiplication of 8
@@ -34,7 +41,12 @@ export function getBigUint(dataView: DataView, byteOffset: number, length: numbe
34
41
  * @param littleEndian If `false` or `undefined`, a big-endian value should be written,
35
42
  * otherwise a little-endian value should be written.
36
43
  */
37
- export function setBigUint(dataView: DataView, byteOffset: number, value: bigint, littleEndian?: boolean) {
44
+ export function setBigUint(
45
+ dataView: DataView,
46
+ byteOffset: number,
47
+ value: bigint,
48
+ littleEndian?: boolean
49
+ ) {
38
50
  const start = byteOffset;
39
51
 
40
52
  if (littleEndian) {
@@ -98,7 +110,7 @@ export function parsePrivateKey(key: Uint8Array): [n: bigint, d: bigint] {
98
110
  // I can't understand, but it does work
99
111
  // Only used with numbers smaller than 2^32 so doesn't need BigInt
100
112
  export function modInverse(a: number, m: number) {
101
- a = (a % m + m) % m;
113
+ a = ((a % m) + m) % m;
102
114
  if (!a || m < 2) {
103
115
  return NaN; // invalid input
104
116
  }
@@ -115,26 +127,24 @@ export function modInverse(a: number, m: number) {
115
127
  // find the inverse
116
128
  let x = 1;
117
129
  let y = 0;
118
- for (let i = s.length - 2; i >= 0; --i) {
130
+ for (let i = s.length - 2; i >= 0; i -= 1) {
119
131
  [x, y] = [y, x - y * Math.floor(s[i]!.a / s[i]!.b)];
120
132
  }
121
- return (y % m + m) % m;
133
+ return ((y % m) + m) % m;
122
134
  }
123
135
 
124
136
  export function calculatePublicKeyLength() {
125
137
  return 4 + 4 + 2048 / 8 + 2048 / 8 + 4;
126
138
  }
127
139
 
128
- export function calculatePublicKey(
129
- privateKey: Uint8Array
130
- ): Uint8Array;
140
+ export function calculatePublicKey(privateKey: Uint8Array): Uint8Array;
131
141
  export function calculatePublicKey(
132
142
  privateKey: Uint8Array,
133
- output: Uint8Array,
143
+ output: Uint8Array
134
144
  ): number;
135
145
  export function calculatePublicKey(
136
146
  privateKey: Uint8Array,
137
- output?: Uint8Array,
147
+ output?: Uint8Array
138
148
  ): Uint8Array | number {
139
149
  // Android has its own public key generation algorithm
140
150
  // See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
@@ -156,20 +166,24 @@ export function calculatePublicKey(
156
166
  // extract `n` from private key
157
167
  const [n] = parsePrivateKey(privateKey);
158
168
 
159
- let outputType: 'Uint8Array' | 'number';
169
+ let outputType: "Uint8Array" | "number";
160
170
  const outputLength = calculatePublicKeyLength();
161
171
  if (!output) {
162
172
  output = new Uint8Array(outputLength);
163
- outputType = 'Uint8Array';
173
+ outputType = "Uint8Array";
164
174
  } else {
165
175
  if (output.byteLength < outputLength) {
166
- throw new Error('output buffer is too small');
176
+ throw new Error("output buffer is too small");
167
177
  }
168
178
 
169
- outputType = 'number';
179
+ outputType = "number";
170
180
  }
171
181
 
172
- const outputView = new DataView(output.buffer, output.byteOffset, output.byteLength);
182
+ const outputView = new DataView(
183
+ output.buffer,
184
+ output.byteOffset,
185
+ output.byteLength
186
+ );
173
187
  let outputOffset = 0;
174
188
 
175
189
  // modulusLengthInWords
@@ -179,7 +193,10 @@ export function calculatePublicKey(
179
193
  // Calculate `n0inv`
180
194
  // Don't know why need to multiple -1
181
195
  // Didn't exist in Android codebase
182
- const n0inv = modInverse(Number(BigInt.asUintN(32, n) * BigInt(-1)), 2 ** 32);
196
+ const n0inv = modInverse(
197
+ Number(BigInt.asUintN(32, n) * BigInt(-1)),
198
+ 2 ** 32
199
+ );
183
200
  outputView.setUint32(outputOffset, n0inv, true);
184
201
  outputOffset += 4;
185
202
 
@@ -188,14 +205,14 @@ export function calculatePublicKey(
188
205
  outputOffset += 256;
189
206
 
190
207
  // Calculate rr = (2^(rsa_size)) ^ 2 mod n
191
- let rr = BigInt(2) ** BigInt(4096) % n;
208
+ const rr = BigInt(2) ** BigInt(4096) % n;
192
209
  outputOffset += setBigUint(outputView, outputOffset, rr, true);
193
210
 
194
211
  // exponent
195
212
  outputView.setUint32(outputOffset, 65537, true);
196
213
  outputOffset += 4;
197
214
 
198
- if (outputType === 'Uint8Array') {
215
+ if (outputType === "Uint8Array") {
199
216
  return output;
200
217
  } else {
201
218
  return outputLength;
@@ -209,7 +226,11 @@ export function calculatePublicKey(
209
226
  *
210
227
  * See https://en.wikipedia.org/wiki/Modular_exponentiation#Implementation_in_Lua
211
228
  */
212
- export function powMod(base: bigint, exponent: bigint, modulus: bigint): bigint {
229
+ export function powMod(
230
+ base: bigint,
231
+ exponent: bigint,
232
+ modulus: bigint
233
+ ): bigint {
213
234
  if (modulus === BigInt1) {
214
235
  return BigInt0;
215
236
  }
@@ -219,7 +240,7 @@ export function powMod(base: bigint, exponent: bigint, modulus: bigint): bigint
219
240
 
220
241
  while (exponent > BigInt0) {
221
242
  if (BigInt.asUintN(1, exponent) === BigInt1) {
222
- r = r * base % modulus;
243
+ r = (r * base) % modulus;
223
244
  }
224
245
 
225
246
  exponent >>= BigInt1;
@@ -238,12 +259,22 @@ export const ASN1_OID = 0x06;
238
259
 
239
260
  // PKCS#1 SHA-1 hash digest info
240
261
  export const SHA1_DIGEST_INFO = new Uint8Array([
241
- ASN1_SEQUENCE, 0x0d + SHA1_DIGEST_LENGTH,
242
- ASN1_SEQUENCE, 0x09,
262
+ ASN1_SEQUENCE,
263
+ 0x0d + SHA1_DIGEST_LENGTH,
264
+ ASN1_SEQUENCE,
265
+ 0x09,
243
266
  // SHA-1 (1 3 14 3 2 26)
244
- ASN1_OID, 0x05, 1 * 40 + 3, 14, 3, 2, 26,
245
- ASN1_NULL, 0x00,
246
- ASN1_OCTET_STRING, SHA1_DIGEST_LENGTH
267
+ ASN1_OID,
268
+ 0x05,
269
+ 1 * 40 + 3,
270
+ 14,
271
+ 3,
272
+ 2,
273
+ 26,
274
+ ASN1_NULL,
275
+ 0x00,
276
+ ASN1_OCTET_STRING,
277
+ SHA1_DIGEST_LENGTH,
247
278
  ]);
248
279
 
249
280
  // SubtleCrypto.sign() will hash the given data and sign the hash
@@ -265,7 +296,8 @@ export function sign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
265
296
  padded[index] = 1;
266
297
  index += 1;
267
298
 
268
- const fillLength = padded.length - SHA1_DIGEST_INFO.length - data.length - 1;
299
+ const fillLength =
300
+ padded.length - SHA1_DIGEST_INFO.length - data.length - 1;
269
301
  while (index < fillLength) {
270
302
  padded[index] = 0xff;
271
303
  index += 1;
@@ -282,11 +314,7 @@ export function sign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
282
314
  // Encryption
283
315
  // signature = padded ** d % n
284
316
  const view = new DataView(padded.buffer);
285
- const signature = powMod(
286
- getBigUint(view, 0, view.byteLength),
287
- d,
288
- n
289
- );
317
+ const signature = powMod(getBigUint(view, 0, view.byteLength), d, n);
290
318
 
291
319
  // `padded` is not used anymore,
292
320
  // re-use the buffer to store the result
package/src/features.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  // The order follows
2
2
  // https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
3
- export enum AdbFeatures {
4
- ShellV2 = 'shell_v2',
5
- Cmd = 'cmd',
6
- StatV2 = 'stat_v2',
7
- ListV2 = 'ls_v2',
8
- FixedPushMkdir = 'fixed_push_mkdir',
3
+ export enum AdbFeature {
4
+ ShellV2 = "shell_v2",
5
+ Cmd = "cmd",
6
+ StatV2 = "stat_v2",
7
+ ListV2 = "ls_v2",
8
+ FixedPushMkdir = "fixed_push_mkdir",
9
+ Abb = "abb",
10
+ AbbExec = "abb_exec",
11
+ SendReceiveV2 = "sendrecv_v2",
9
12
  }
package/src/index.ts CHANGED
@@ -1,10 +1,9 @@
1
-
2
- export * from './adb.js';
3
- export * from './auth.js';
4
- export * from './backend.js';
5
- export * from './commands/index.js';
6
- export * from './crypto.js';
7
- export * from './features.js';
8
- export * from './packet.js';
9
- export * from './socket/index.js';
10
- export * from './utils/index.js';
1
+ export * from "./adb.js";
2
+ export * from "./auth.js";
3
+ export * from "./backend.js";
4
+ export * from "./commands/index.js";
5
+ export * from "./crypto.js";
6
+ export * from "./features.js";
7
+ export * from "./packet.js";
8
+ export * from "./socket/index.js";
9
+ export * from "./utils/index.js";
package/src/packet.ts CHANGED
@@ -1,34 +1,32 @@
1
- import { TransformStream } from '@yume-chan/stream-extra';
2
- import Struct from '@yume-chan/struct';
1
+ import { ConsumableTransformStream } from "@yume-chan/stream-extra";
2
+ import Struct from "@yume-chan/struct";
3
3
 
4
4
  export enum AdbCommand {
5
- Auth = 0x48545541, // 'AUTH'
6
- Close = 0x45534c43, // 'CLSE'
5
+ Auth = 0x48545541, // 'AUTH'
6
+ Close = 0x45534c43, // 'CLSE'
7
7
  Connect = 0x4e584e43, // 'CNXN'
8
- OK = 0x59414b4f, // 'OKAY'
9
- Open = 0x4e45504f, // 'OPEN'
10
- Write = 0x45545257, // 'WRTE'
8
+ OK = 0x59414b4f, // 'OKAY'
9
+ Open = 0x4e45504f, // 'OPEN'
10
+ Write = 0x45545257, // 'WRTE'
11
11
  }
12
12
 
13
- export const AdbPacketHeader =
14
- new Struct({ littleEndian: true })
15
- .uint32('command')
16
- .uint32('arg0')
17
- .uint32('arg1')
18
- .uint32('payloadLength')
19
- .uint32('checksum')
20
- .int32('magic');
13
+ export const AdbPacketHeader = new Struct({ littleEndian: true })
14
+ .uint32("command")
15
+ .uint32("arg0")
16
+ .uint32("arg1")
17
+ .uint32("payloadLength")
18
+ .uint32("checksum")
19
+ .int32("magic");
21
20
 
22
- export type AdbPacketHeader = typeof AdbPacketHeader['TDeserializeResult'];
21
+ export type AdbPacketHeader = (typeof AdbPacketHeader)["TDeserializeResult"];
23
22
 
24
- type AdbPacketHeaderInit = typeof AdbPacketHeader['TInit'];
23
+ type AdbPacketHeaderInit = (typeof AdbPacketHeader)["TInit"];
25
24
 
26
- export const AdbPacket =
27
- new Struct({ littleEndian: true })
28
- .fields(AdbPacketHeader)
29
- .uint8Array('payload', { lengthField: 'payloadLength' });
25
+ export const AdbPacket = new Struct({ littleEndian: true })
26
+ .fields(AdbPacketHeader)
27
+ .uint8Array("payload", { lengthField: "payloadLength" });
30
28
 
31
- export type AdbPacket = typeof AdbPacket['TDeserializeResult'];
29
+ export type AdbPacket = (typeof AdbPacket)["TDeserializeResult"];
32
30
 
33
31
  /**
34
32
  * `AdbPacketData` contains all the useful fields of `AdbPacket`.
@@ -40,39 +38,36 @@ export type AdbPacket = typeof AdbPacket['TDeserializeResult'];
40
38
  * however, `AdbPacketDispatcher` will transform `AdbPacketData` to `AdbPacketInit` for you,
41
39
  * so `AdbSocket#writable#write` only needs `AdbPacketData`.
42
40
  */
43
- export type AdbPacketData = Omit<typeof AdbPacket['TInit'], 'checksum' | 'magic'>;
41
+ export type AdbPacketData = Omit<
42
+ (typeof AdbPacket)["TInit"],
43
+ "checksum" | "magic"
44
+ >;
44
45
 
45
- // All fields except `magic`, which can be calculated in `AdbPacketSerializeStream`
46
- export type AdbPacketInit = Omit<typeof AdbPacket['TInit'], 'magic'>;
46
+ export type AdbPacketInit = (typeof AdbPacket)["TInit"];
47
47
 
48
- export function calculateChecksum(payload: Uint8Array): number;
49
- export function calculateChecksum(init: AdbPacketData): AdbPacketInit;
50
- export function calculateChecksum(payload: Uint8Array | AdbPacketData): number | AdbPacketInit {
51
- if (payload instanceof Uint8Array) {
52
- return payload.reduce((result, item) => result + item, 0);
53
- } else {
54
- (payload as AdbPacketInit).checksum = calculateChecksum(payload.payload);
55
- return payload as AdbPacketInit;
56
- }
48
+ export function calculateChecksum(payload: Uint8Array): number {
49
+ return payload.reduce((result, item) => result + item, 0);
57
50
  }
58
51
 
59
- export class AdbPacketSerializeStream extends TransformStream<AdbPacketInit, Uint8Array>{
52
+ export class AdbPacketSerializeStream extends ConsumableTransformStream<
53
+ AdbPacketInit,
54
+ Uint8Array
55
+ > {
60
56
  public constructor() {
57
+ const headerBuffer = new Uint8Array(AdbPacketHeader.size);
61
58
  super({
62
- transform: async (init, controller) => {
63
- // This syntax is ugly, but I don't want to create a new object.
64
- (init as unknown as AdbPacketHeaderInit).magic = init.command ^ 0xFFFFFFFF;
65
- (init as unknown as AdbPacketHeaderInit).payloadLength = init.payload.byteLength;
59
+ transform: async (chunk, controller) => {
60
+ const init = chunk as AdbPacketInit & AdbPacketHeaderInit;
61
+ init.payloadLength = init.payload.byteLength;
66
62
 
67
- controller.enqueue(
68
- AdbPacketHeader.serialize(
69
- init as unknown as AdbPacketHeaderInit
70
- )
71
- );
63
+ AdbPacketHeader.serialize(init, headerBuffer);
64
+ await controller.enqueue(headerBuffer);
72
65
 
73
66
  if (init.payload.byteLength) {
74
- // Enqueue payload separately to avoid copying
75
- controller.enqueue(init.payload);
67
+ // USB protocol preserves packet boundaries,
68
+ // so we must write payload separately as native ADB does,
69
+ // otherwise the read operation on device will fail.
70
+ await controller.enqueue(init.payload);
76
71
  }
77
72
  },
78
73
  });