appium-adb 14.1.0 → 14.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/lib/adb.d.ts.map +1 -1
- package/build/lib/adb.js.map +1 -1
- package/build/lib/helpers.d.ts.map +1 -1
- package/build/lib/helpers.js +3 -3
- package/build/lib/helpers.js.map +1 -1
- package/build/lib/logcat.d.ts.map +1 -1
- package/build/lib/logcat.js +16 -6
- package/build/lib/logcat.js.map +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/tools/aab-utils.d.ts.map +1 -1
- package/build/lib/tools/aab-utils.js +25 -18
- package/build/lib/tools/aab-utils.js.map +1 -1
- package/build/lib/tools/android-manifest.d.ts.map +1 -1
- package/build/lib/tools/android-manifest.js +36 -24
- package/build/lib/tools/android-manifest.js.map +1 -1
- package/build/lib/tools/apk-signing.d.ts.map +1 -1
- package/build/lib/tools/apk-signing.js +58 -37
- package/build/lib/tools/apk-signing.js.map +1 -1
- package/build/lib/tools/apk-utils.d.ts.map +1 -1
- package/build/lib/tools/apk-utils.js +46 -38
- package/build/lib/tools/apk-utils.js.map +1 -1
- package/build/lib/tools/apks-utils.d.ts.map +1 -1
- package/build/lib/tools/apks-utils.js +21 -14
- package/build/lib/tools/apks-utils.js.map +1 -1
- package/build/lib/tools/app-commands.d.ts.map +1 -1
- package/build/lib/tools/app-commands.js +44 -45
- package/build/lib/tools/app-commands.js.map +1 -1
- package/build/lib/tools/device-settings.d.ts.map +1 -1
- package/build/lib/tools/device-settings.js +38 -32
- package/build/lib/tools/device-settings.js.map +1 -1
- package/build/lib/tools/emu-constants.d.ts.map +1 -1
- package/build/lib/tools/emu-constants.js +4 -4
- package/build/lib/tools/emu-constants.js.map +1 -1
- package/build/lib/tools/emulator-commands.d.ts.map +1 -1
- package/build/lib/tools/emulator-commands.js +20 -17
- package/build/lib/tools/emulator-commands.js.map +1 -1
- package/build/lib/tools/fs-commands.d.ts.map +1 -1
- package/build/lib/tools/fs-commands.js +2 -1
- package/build/lib/tools/fs-commands.js.map +1 -1
- package/build/lib/tools/general-commands.d.ts.map +1 -1
- package/build/lib/tools/general-commands.js +11 -15
- package/build/lib/tools/general-commands.js.map +1 -1
- package/build/lib/tools/keyboard-commands.d.ts.map +1 -1
- package/build/lib/tools/keyboard-commands.js.map +1 -1
- package/build/lib/tools/lockmgmt.d.ts.map +1 -1
- package/build/lib/tools/lockmgmt.js +24 -23
- package/build/lib/tools/lockmgmt.js.map +1 -1
- package/build/lib/tools/logcat-commands.d.ts.map +1 -1
- package/build/lib/tools/logcat-commands.js.map +1 -1
- package/build/lib/tools/network-commands.d.ts.map +1 -1
- package/build/lib/tools/network-commands.js +0 -1
- package/build/lib/tools/network-commands.js.map +1 -1
- package/build/lib/tools/process-commands.d.ts.map +1 -1
- package/build/lib/tools/process-commands.js +1 -1
- package/build/lib/tools/process-commands.js.map +1 -1
- package/build/lib/tools/system-calls.d.ts.map +1 -1
- package/build/lib/tools/system-calls.js +30 -36
- package/build/lib/tools/system-calls.js.map +1 -1
- package/build/lib/tools/types.d.ts +6 -6
- package/build/lib/tools/types.d.ts.map +1 -1
- package/lib/adb.ts +20 -25
- package/lib/helpers.ts +48 -36
- package/lib/logcat.ts +32 -22
- package/lib/logger.ts +1 -2
- package/lib/tools/aab-utils.js +42 -33
- package/lib/tools/android-manifest.js +78 -54
- package/lib/tools/apk-signing.ts +123 -89
- package/lib/tools/apk-utils.ts +206 -109
- package/lib/tools/apks-utils.js +50 -40
- package/lib/tools/app-commands.ts +220 -135
- package/lib/tools/device-settings.ts +154 -124
- package/lib/tools/emu-constants.ts +4 -5
- package/lib/tools/emulator-commands.ts +113 -65
- package/lib/tools/fs-commands.js +9 -8
- package/lib/tools/general-commands.js +48 -51
- package/lib/tools/keyboard-commands.js +22 -20
- package/lib/tools/lockmgmt.js +80 -51
- package/lib/tools/logcat-commands.js +6 -6
- package/lib/tools/network-commands.js +11 -12
- package/lib/tools/process-commands.ts +28 -15
- package/lib/tools/system-calls.ts +319 -221
- package/lib/tools/types.ts +19 -10
- package/package.json +4 -3
package/lib/helpers.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {system, fs, zip, util} from '@appium/support';
|
|
3
|
+
import {log} from './logger.js';
|
|
4
4
|
import _ from 'lodash';
|
|
5
|
-
import {
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
5
|
+
import {exec, type ExecError} from 'teen_process';
|
|
6
|
+
import type {ADB} from './adb.js';
|
|
7
|
+
import type {ApkManifest} from './tools/types.js';
|
|
8
8
|
|
|
9
9
|
// Declare __filename for CommonJS compatibility
|
|
10
10
|
declare const __filename: string;
|
|
@@ -21,12 +21,16 @@ const MODULE_NAME = 'appium-adb';
|
|
|
21
21
|
/**
|
|
22
22
|
* Calculates the absolute path to the given resource
|
|
23
23
|
*/
|
|
24
|
-
export const getResourcePath = _.memoize(async function getResourcePath
|
|
24
|
+
export const getResourcePath = _.memoize(async function getResourcePath(
|
|
25
|
+
relPath: string,
|
|
26
|
+
): Promise<string> {
|
|
25
27
|
const moduleRoot = await getModuleRoot();
|
|
26
28
|
const resultPath = path.resolve(moduleRoot, relPath);
|
|
27
|
-
if (!await fs.exists(resultPath)) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`
|
|
29
|
+
if (!(await fs.exists(resultPath))) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Cannot find the resource '${relPath}' under the '${moduleRoot}' ` +
|
|
32
|
+
`folder of ${MODULE_NAME} Node.js module`,
|
|
33
|
+
);
|
|
30
34
|
}
|
|
31
35
|
return resultPath;
|
|
32
36
|
});
|
|
@@ -34,21 +38,26 @@ export const getResourcePath = _.memoize(async function getResourcePath (relPath
|
|
|
34
38
|
/**
|
|
35
39
|
* Retrieves the actual path to SDK root folder from the system environment
|
|
36
40
|
*/
|
|
37
|
-
export function getSdkRootFromEnv
|
|
41
|
+
export function getSdkRootFromEnv(): string | undefined {
|
|
38
42
|
return process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
46
|
* Retrieves the actual path to SDK root folder
|
|
43
47
|
*/
|
|
44
|
-
export async function requireSdkRoot
|
|
48
|
+
export async function requireSdkRoot(customRoot: string | null = null): Promise<string> {
|
|
45
49
|
const sdkRoot = customRoot || getSdkRootFromEnv();
|
|
46
|
-
const docMsg =
|
|
50
|
+
const docMsg =
|
|
51
|
+
'Read https://developer.android.com/studio/command-line/variables for more details';
|
|
47
52
|
if (!sdkRoot || _.isEmpty(sdkRoot)) {
|
|
48
|
-
throw new Error(
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Neither ANDROID_HOME nor ANDROID_SDK_ROOT environment variable was exported. ${docMsg}`,
|
|
55
|
+
);
|
|
49
56
|
}
|
|
50
|
-
if (!await fs.exists(sdkRoot)) {
|
|
51
|
-
throw new Error(
|
|
57
|
+
if (!(await fs.exists(sdkRoot))) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`The Android SDK root folder '${sdkRoot}' does not exist on the local file system. ${docMsg}`,
|
|
60
|
+
);
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
const stats = await fs.stat(sdkRoot);
|
|
@@ -62,19 +71,22 @@ export async function requireSdkRoot (customRoot: string | null = null): Promise
|
|
|
62
71
|
* @param zipPath
|
|
63
72
|
* @param dstRoot
|
|
64
73
|
*/
|
|
65
|
-
export async function unzipFile
|
|
74
|
+
export async function unzipFile(
|
|
75
|
+
zipPath: string,
|
|
76
|
+
dstRoot: string = path.dirname(zipPath),
|
|
77
|
+
): Promise<void> {
|
|
66
78
|
log.debug(`Unzipping '${zipPath}' to '${dstRoot}'`);
|
|
67
79
|
await zip.assertValidZip(zipPath);
|
|
68
80
|
await zip.extractAllTo(zipPath, dstRoot);
|
|
69
81
|
log.debug('Unzip successful');
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
export const getJavaHome = _.memoize(async function getJavaHome
|
|
84
|
+
export const getJavaHome = _.memoize(async function getJavaHome(): Promise<string> {
|
|
73
85
|
const result = process.env.JAVA_HOME;
|
|
74
86
|
if (!result) {
|
|
75
87
|
throw new Error('The JAVA_HOME environment variable is not set for the current process');
|
|
76
88
|
}
|
|
77
|
-
if (!await fs.exists(result)) {
|
|
89
|
+
if (!(await fs.exists(result))) {
|
|
78
90
|
throw new Error(`The JAVA_HOME location '${result}' must exist`);
|
|
79
91
|
}
|
|
80
92
|
const stats = await fs.stat(result);
|
|
@@ -84,7 +96,7 @@ export const getJavaHome = _.memoize(async function getJavaHome (): Promise<stri
|
|
|
84
96
|
return result;
|
|
85
97
|
});
|
|
86
98
|
|
|
87
|
-
export const getJavaForOs = _.memoize(async function getJavaForOs
|
|
99
|
+
export const getJavaForOs = _.memoize(async function getJavaForOs(): Promise<string> {
|
|
88
100
|
let javaHome: string | undefined;
|
|
89
101
|
let errMsg: string | undefined;
|
|
90
102
|
try {
|
|
@@ -105,16 +117,18 @@ export const getJavaForOs = _.memoize(async function getJavaForOs (): Promise<st
|
|
|
105
117
|
} catch {
|
|
106
118
|
// Ignore and throw custom error below
|
|
107
119
|
}
|
|
108
|
-
throw new Error(
|
|
109
|
-
`
|
|
120
|
+
throw new Error(
|
|
121
|
+
`The '${executableName}' binary could not be found ` +
|
|
122
|
+
`neither in PATH nor under JAVA_HOME (${javaHome ? path.resolve(javaHome, 'bin') : errMsg})`,
|
|
123
|
+
);
|
|
110
124
|
});
|
|
111
125
|
|
|
112
126
|
/**
|
|
113
127
|
* Transforms given options into the list of `adb install.install-multiple` command arguments
|
|
114
128
|
*/
|
|
115
|
-
export function buildInstallArgs
|
|
129
|
+
export function buildInstallArgs(
|
|
116
130
|
apiLevel: number,
|
|
117
|
-
options: BuildInstallArgsOptions = {}
|
|
131
|
+
options: BuildInstallArgsOptions = {},
|
|
118
132
|
): string[] {
|
|
119
133
|
const result: string[] = [];
|
|
120
134
|
|
|
@@ -129,9 +143,11 @@ export function buildInstallArgs (
|
|
|
129
143
|
}
|
|
130
144
|
if (options.grantPermissions) {
|
|
131
145
|
if (apiLevel < 23) {
|
|
132
|
-
log.debug(
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
log.debug(
|
|
147
|
+
`Skipping permissions grant option, since ` +
|
|
148
|
+
`the current API level ${apiLevel} does not support applications ` +
|
|
149
|
+
`permissions customization`,
|
|
150
|
+
);
|
|
135
151
|
} else {
|
|
136
152
|
result.push('-g');
|
|
137
153
|
}
|
|
@@ -148,7 +164,7 @@ export function buildInstallArgs (
|
|
|
148
164
|
* Extracts various package manifest details
|
|
149
165
|
* from the given application file.
|
|
150
166
|
*/
|
|
151
|
-
export async function readPackageManifest
|
|
167
|
+
export async function readPackageManifest(this: ADB, apkPath: string): Promise<ApkManifest> {
|
|
152
168
|
await this.initAapt2();
|
|
153
169
|
const aapt2Binary = this.binaries?.aapt2;
|
|
154
170
|
if (!aapt2Binary) {
|
|
@@ -173,7 +189,7 @@ export async function readPackageManifest (this: ADB, apkPath: string): Promise<
|
|
|
173
189
|
const extractValue = (
|
|
174
190
|
line: string,
|
|
175
191
|
propPattern: RegExp,
|
|
176
|
-
valueTransformer: ((x: string) => any) | null
|
|
192
|
+
valueTransformer: ((x: string) => any) | null,
|
|
177
193
|
): any => {
|
|
178
194
|
const match = propPattern.exec(line);
|
|
179
195
|
if (match) {
|
|
@@ -184,7 +200,7 @@ export async function readPackageManifest (this: ADB, apkPath: string): Promise<
|
|
|
184
200
|
const extractArray = (
|
|
185
201
|
line: string,
|
|
186
202
|
propPattern: RegExp,
|
|
187
|
-
valueTransformer: ((x: string) => any) | null
|
|
203
|
+
valueTransformer: ((x: string) => any) | null,
|
|
188
204
|
): any[] => {
|
|
189
205
|
let match: RegExpExecArray | null;
|
|
190
206
|
const resultArray: any[] = [];
|
|
@@ -220,11 +236,7 @@ export async function readPackageManifest (this: ADB, apkPath: string): Promise<
|
|
|
220
236
|
['compileSdkVersion', /compileSdkVersion='([^']+)'/, toInt],
|
|
221
237
|
['compileSdkVersionCodename', /compileSdkVersionCodename='([^']+)'/, null],
|
|
222
238
|
] as const) {
|
|
223
|
-
const value = extractValue(
|
|
224
|
-
line,
|
|
225
|
-
pattern,
|
|
226
|
-
transformer
|
|
227
|
-
);
|
|
239
|
+
const value = extractValue(line, pattern, transformer);
|
|
228
240
|
if (!_.isUndefined(value)) {
|
|
229
241
|
(result as Record<string, any>)[name] = value;
|
|
230
242
|
}
|
|
@@ -271,7 +283,7 @@ export async function readPackageManifest (this: ADB, apkPath: string): Promise<
|
|
|
271
283
|
/**
|
|
272
284
|
* Calculates the absolute path to the current module's root folder
|
|
273
285
|
*/
|
|
274
|
-
const getModuleRoot = _.memoize(async function getModuleRoot
|
|
286
|
+
const getModuleRoot = _.memoize(async function getModuleRoot(): Promise<string> {
|
|
275
287
|
let moduleRoot = path.dirname(path.resolve(__filename));
|
|
276
288
|
let isAtFsRoot = false;
|
|
277
289
|
while (!isAtFsRoot) {
|
|
@@ -279,7 +291,7 @@ const getModuleRoot = _.memoize(async function getModuleRoot (): Promise<string>
|
|
|
279
291
|
try {
|
|
280
292
|
if (await fs.exists(manifestPath)) {
|
|
281
293
|
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
282
|
-
const manifest = JSON.parse(manifestContent) as {
|
|
294
|
+
const manifest = JSON.parse(manifestContent) as {name?: string};
|
|
283
295
|
if (manifest.name === MODULE_NAME) {
|
|
284
296
|
return moduleRoot;
|
|
285
297
|
}
|
package/lib/logcat.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {logger, util} from '@appium/support';
|
|
2
2
|
import B from 'bluebird';
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
LogEntry,
|
|
11
|
-
LogcatOpts as StartCaptureOptions,
|
|
12
|
-
} from './tools/types';
|
|
4
|
+
import {EventEmitter} from 'node:events';
|
|
5
|
+
import {SubProcess, exec} from 'teen_process';
|
|
6
|
+
import {LRUCache} from 'lru-cache';
|
|
7
|
+
import type {ExecError} from 'teen_process';
|
|
8
|
+
import type {ADBExecutable} from './types';
|
|
9
|
+
import type {LogEntry, LogcatOpts as StartCaptureOptions} from './tools/types';
|
|
13
10
|
|
|
14
11
|
const log = logger.getLogger('Logcat');
|
|
15
12
|
const MAX_BUFFER_SIZE = 10000;
|
|
16
13
|
const LOGCAT_PROC_STARTUP_TIMEOUT = 10000;
|
|
17
|
-
const SUPPORTED_FORMATS = [
|
|
14
|
+
const SUPPORTED_FORMATS = [
|
|
15
|
+
'brief',
|
|
16
|
+
'process',
|
|
17
|
+
'tag',
|
|
18
|
+
'thread',
|
|
19
|
+
'raw',
|
|
20
|
+
'time',
|
|
21
|
+
'threadtime',
|
|
22
|
+
'long',
|
|
23
|
+
] as const;
|
|
18
24
|
const SUPPORTED_PRIORITIES = ['v', 'd', 'i', 'w', 'e', 'f', 's'] as const;
|
|
19
25
|
const DEFAULT_PRIORITY = 'v';
|
|
20
26
|
const DEFAULT_TAG = '*';
|
|
@@ -70,14 +76,12 @@ export class Logcat extends EventEmitter {
|
|
|
70
76
|
await this.clear();
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
const {
|
|
74
|
-
format = DEFAULT_FORMAT,
|
|
75
|
-
filterSpecs = [],
|
|
76
|
-
} = opts;
|
|
79
|
+
const {format = DEFAULT_FORMAT, filterSpecs = []} = opts;
|
|
77
80
|
const cmd = [
|
|
78
81
|
...this.adb.defaultArgs,
|
|
79
82
|
'logcat',
|
|
80
|
-
'-v',
|
|
83
|
+
'-v',
|
|
84
|
+
requireFormat(format),
|
|
81
85
|
...formatFilterSpecs(filterSpecs),
|
|
82
86
|
];
|
|
83
87
|
log.debug(`Starting logs capture with command: ${util.quote([this.adb.path, ...cmd])}`);
|
|
@@ -129,8 +133,10 @@ export class Logcat extends EventEmitter {
|
|
|
129
133
|
continue;
|
|
130
134
|
}
|
|
131
135
|
const [message, timestamp] = value;
|
|
132
|
-
if (
|
|
133
|
-
|
|
136
|
+
if (
|
|
137
|
+
(this.logIndexSinceLastRequest && index > this.logIndexSinceLastRequest) ||
|
|
138
|
+
!this.logIndexSinceLastRequest
|
|
139
|
+
) {
|
|
134
140
|
recentLogIndex = index;
|
|
135
141
|
result.push(toLogEntry(message, timestamp));
|
|
136
142
|
}
|
|
@@ -185,7 +191,7 @@ export default Logcat;
|
|
|
185
191
|
|
|
186
192
|
// Private entities
|
|
187
193
|
|
|
188
|
-
type LogFormat = typeof SUPPORTED_FORMATS[number];
|
|
194
|
+
type LogFormat = (typeof SUPPORTED_FORMATS)[number];
|
|
189
195
|
|
|
190
196
|
function requireFormat(format: string): LogFormat {
|
|
191
197
|
if (!SUPPORTED_FORMATS.includes(format as LogFormat)) {
|
|
@@ -213,11 +219,15 @@ function requireSpec(spec: string): string {
|
|
|
213
219
|
resultTag = DEFAULT_TAG;
|
|
214
220
|
}
|
|
215
221
|
if (!priority) {
|
|
216
|
-
log.info(
|
|
222
|
+
log.info(
|
|
223
|
+
`The priority value in spec '${spec}' is empty. Defaulting to Verbose (${DEFAULT_PRIORITY})`,
|
|
224
|
+
);
|
|
217
225
|
return `${resultTag}:${DEFAULT_PRIORITY}`;
|
|
218
226
|
}
|
|
219
227
|
if (!SUPPORTED_PRIORITIES.some((p) => _.toLower(priority) === _.toLower(p))) {
|
|
220
|
-
log.info(
|
|
228
|
+
log.info(
|
|
229
|
+
`The priority value in spec '${spec}' is unknown. Supported values are: ${SUPPORTED_PRIORITIES}`,
|
|
230
|
+
);
|
|
221
231
|
log.info(`Defaulting to Verbose (${DEFAULT_PRIORITY})`);
|
|
222
232
|
return `${resultTag}:${DEFAULT_PRIORITY}`;
|
|
223
233
|
}
|
|
@@ -230,5 +240,5 @@ function formatFilterSpecs(filterSpecs: string | string[]): string[] {
|
|
|
230
240
|
}
|
|
231
241
|
return filterSpecs
|
|
232
242
|
.filter((spec) => spec && _.isString(spec) && !spec.startsWith('-'))
|
|
233
|
-
.map((spec) => spec.includes(':') ? requireSpec(spec) : spec);
|
|
243
|
+
.map((spec) => (spec.includes(':') ? requireSpec(spec) : spec));
|
|
234
244
|
}
|
package/lib/logger.ts
CHANGED
package/lib/tools/aab-utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {log} from '../logger.js';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import {fs, tempDir, util} from '@appium/support';
|
|
4
|
+
import {LRUCache} from 'lru-cache';
|
|
5
|
+
import {unzipFile} from '../helpers.js';
|
|
6
6
|
import AsyncLock from 'async-lock';
|
|
7
7
|
import B from 'bluebird';
|
|
8
8
|
import crypto from 'crypto';
|
|
@@ -21,19 +21,19 @@ process.on('exit', () => {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const paths = /** @type {string[]} */ ([...AAB_CACHE.values()]);
|
|
24
|
-
log.debug(
|
|
25
|
-
util.pluralize('package', paths.length)
|
|
24
|
+
log.debug(
|
|
25
|
+
`Performing cleanup of ${paths.length} cached .aab ` + util.pluralize('package', paths.length),
|
|
26
|
+
);
|
|
26
27
|
for (const appPath of paths) {
|
|
27
28
|
try {
|
|
28
29
|
// Asynchronous calls are not supported in onExit handler
|
|
29
30
|
fs.rimrafSync(appPath);
|
|
30
31
|
} catch (e) {
|
|
31
|
-
log.warn(
|
|
32
|
+
log.warn(/** @type {Error} */ (e).message);
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
});
|
|
35
36
|
|
|
36
|
-
|
|
37
37
|
/**
|
|
38
38
|
* Builds a universal .apk from the given .aab package. See
|
|
39
39
|
* https://developer.android.com/studio/command-line/bundletool#generate_apks
|
|
@@ -46,8 +46,8 @@ process.on('exit', () => {
|
|
|
46
46
|
* by default.
|
|
47
47
|
* @throws {Error} If there was an error while creating the universal .apk
|
|
48
48
|
*/
|
|
49
|
-
export async function extractUniversalApk
|
|
50
|
-
if (!await fs.exists(aabPath)) {
|
|
49
|
+
export async function extractUniversalApk(aabPath, opts = {}) {
|
|
50
|
+
if (!(await fs.exists(aabPath))) {
|
|
51
51
|
throw new Error(`The file at '${aabPath}' either does not exist or is not accessible`);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -58,21 +58,19 @@ export async function extractUniversalApk (aabPath, opts = {}) {
|
|
|
58
58
|
try {
|
|
59
59
|
return await AAB_CACHE_GUARD.acquire(aabPath, async () => {
|
|
60
60
|
const aabHash = await fs.hash(aabPath);
|
|
61
|
-
const {
|
|
62
|
-
keystore,
|
|
63
|
-
keystorePassword,
|
|
64
|
-
keyAlias,
|
|
65
|
-
keyPassword,
|
|
66
|
-
} = opts;
|
|
61
|
+
const {keystore, keystorePassword, keyAlias, keyPassword} = opts;
|
|
67
62
|
let cacheHash = aabHash;
|
|
68
63
|
if (keystore) {
|
|
69
|
-
if (!await fs.exists(keystore)) {
|
|
70
|
-
throw new Error(
|
|
71
|
-
`or is not accessible
|
|
64
|
+
if (!(await fs.exists(keystore))) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`The keystore file at '${keystore}' either does not exist ` + `or is not accessible`,
|
|
67
|
+
);
|
|
72
68
|
}
|
|
73
69
|
if (!keystorePassword || !keyAlias || !keyPassword) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
'
|
|
70
|
+
throw new Error(
|
|
71
|
+
'It is mandatory to also provide keystore password, key alias, ' +
|
|
72
|
+
'and key password if the keystore path is set',
|
|
73
|
+
);
|
|
76
74
|
}
|
|
77
75
|
const keystoreHash = await fs.hash(keystore);
|
|
78
76
|
const keyAliasHash = crypto.createHash('sha1');
|
|
@@ -91,16 +89,25 @@ export async function extractUniversalApk (aabPath, opts = {}) {
|
|
|
91
89
|
await this.initAapt2();
|
|
92
90
|
const args = [
|
|
93
91
|
'build-apks',
|
|
94
|
-
'--aapt2',
|
|
95
|
-
'
|
|
96
|
-
'--
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
'--aapt2',
|
|
93
|
+
/** @type {import('./types').StringRecord} */ (this.binaries).aapt2,
|
|
94
|
+
'--bundle',
|
|
95
|
+
aabPath,
|
|
96
|
+
'--output',
|
|
97
|
+
tmpApksPath,
|
|
98
|
+
...(keystore
|
|
99
|
+
? [
|
|
100
|
+
'--ks',
|
|
101
|
+
keystore,
|
|
102
|
+
'--ks-pass',
|
|
103
|
+
`pass:${keystorePassword}`,
|
|
104
|
+
'--ks-key-alias',
|
|
105
|
+
keyAlias,
|
|
106
|
+
'--key-pass',
|
|
107
|
+
`pass:${keyPassword}`,
|
|
108
|
+
]
|
|
109
|
+
: []),
|
|
110
|
+
'--mode=universal',
|
|
104
111
|
];
|
|
105
112
|
log.debug(`Preparing universal .apks bundle from '${aabPath}'`);
|
|
106
113
|
await this.execBundletool(args, `Cannot build a universal .apks bundle from '${aabPath}'`);
|
|
@@ -123,8 +130,10 @@ export async function extractUniversalApk (aabPath, opts = {}) {
|
|
|
123
130
|
} catch {}
|
|
124
131
|
if (!universalApkPath) {
|
|
125
132
|
log.debug(`The following items were extracted from the .aab bundle: ${allFileNames}`);
|
|
126
|
-
throw new Error(
|
|
127
|
-
|
|
133
|
+
throw new Error(
|
|
134
|
+
`${UNIVERSAL_APK} cannot be found in '${aabPath}' bundle. ` +
|
|
135
|
+
`Does the archive contain a valid application bundle?`,
|
|
136
|
+
);
|
|
128
137
|
}
|
|
129
138
|
const resultPath = path.join(tmpRoot, apkName);
|
|
130
139
|
log.debug(`Found ${UNIVERSAL_APK} at '${universalApkPath}'. Caching it to '${resultPath}'`);
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
APKS_EXTENSION,
|
|
7
|
-
readPackageManifest,
|
|
8
|
-
} from '../helpers.js';
|
|
9
|
-
import { fs, zip, tempDir, util } from '@appium/support';
|
|
2
|
+
import {exec} from 'teen_process';
|
|
3
|
+
import {log} from '../logger.js';
|
|
4
|
+
import {unzipFile, APKS_EXTENSION, readPackageManifest} from '../helpers.js';
|
|
5
|
+
import {fs, zip, tempDir, util} from '@appium/support';
|
|
10
6
|
import path from 'path';
|
|
11
7
|
|
|
12
8
|
/**
|
|
@@ -18,16 +14,14 @@ import path from 'path';
|
|
|
18
14
|
* @throws {error} If there was an error while getting the data from the given
|
|
19
15
|
* application package.
|
|
20
16
|
*/
|
|
21
|
-
export async function packageAndLaunchActivityFromManifest
|
|
17
|
+
export async function packageAndLaunchActivityFromManifest(appPath) {
|
|
22
18
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
23
19
|
appPath = await this.extractBaseApk(appPath);
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
const {
|
|
27
23
|
name: apkPackage,
|
|
28
|
-
launchableActivity: {
|
|
29
|
-
name: apkActivity,
|
|
30
|
-
},
|
|
24
|
+
launchableActivity: {name: apkActivity},
|
|
31
25
|
} = await readPackageManifest.bind(this)(appPath);
|
|
32
26
|
log.info(`Package name: '${apkPackage}'`);
|
|
33
27
|
log.info(`Main activity name: '${apkActivity}'`);
|
|
@@ -43,7 +37,7 @@ export async function packageAndLaunchActivityFromManifest (appPath) {
|
|
|
43
37
|
* @throws {error} If there was an error while getting the data from the given
|
|
44
38
|
* application package.
|
|
45
39
|
*/
|
|
46
|
-
export async function targetSdkVersionFromManifest
|
|
40
|
+
export async function targetSdkVersionFromManifest(appPath) {
|
|
47
41
|
log.debug(`Extracting target SDK version of '${appPath}'`);
|
|
48
42
|
const originalAppPath = appPath;
|
|
49
43
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
@@ -54,7 +48,7 @@ export async function targetSdkVersionFromManifest (appPath) {
|
|
|
54
48
|
if (!targetSdkVersion) {
|
|
55
49
|
throw new Error(
|
|
56
50
|
`Cannot extract targetSdkVersion of '${originalAppPath}'. Does ` +
|
|
57
|
-
|
|
51
|
+
`the package manifest define it?`,
|
|
58
52
|
);
|
|
59
53
|
}
|
|
60
54
|
return targetSdkVersion;
|
|
@@ -69,8 +63,8 @@ export async function targetSdkVersionFromManifest (appPath) {
|
|
|
69
63
|
* _dumpsys package_ command. It may speed up the method execution.
|
|
70
64
|
* @return {Promise<number>} The version of the target SDK.
|
|
71
65
|
*/
|
|
72
|
-
export async function targetSdkVersionUsingPKG
|
|
73
|
-
const stdout = cmdOutput || await this.shell(['dumpsys', 'package', pkg]);
|
|
66
|
+
export async function targetSdkVersionUsingPKG(pkg, cmdOutput = null) {
|
|
67
|
+
const stdout = cmdOutput || (await this.shell(['dumpsys', 'package', pkg]));
|
|
74
68
|
const targetSdkVersionMatch = new RegExp(/targetSdk=([^\s\s]+)/g).exec(stdout);
|
|
75
69
|
return targetSdkVersionMatch && targetSdkVersionMatch.length >= 2
|
|
76
70
|
? parseInt(targetSdkVersionMatch[1], 10)
|
|
@@ -87,10 +81,14 @@ export async function targetSdkVersionUsingPKG (pkg, cmdOutput = null) {
|
|
|
87
81
|
* @param {string} manifestPackage - The name of the manifest package
|
|
88
82
|
* @param {string} targetPackage - The name of the destination package
|
|
89
83
|
*/
|
|
90
|
-
export async function compileManifest
|
|
91
|
-
const {platform, platformPath} = await getAndroidPlatformAndPath(
|
|
84
|
+
export async function compileManifest(manifest, manifestPackage, targetPackage) {
|
|
85
|
+
const {platform, platformPath} = await getAndroidPlatformAndPath(
|
|
86
|
+
/** @type {string} */ (this.sdkRoot),
|
|
87
|
+
);
|
|
92
88
|
if (!platform || !platformPath) {
|
|
93
|
-
throw new Error(
|
|
89
|
+
throw new Error(
|
|
90
|
+
'Cannot compile the manifest. The required platform does not exist (API level >= 17)',
|
|
91
|
+
);
|
|
94
92
|
}
|
|
95
93
|
const resultPath = `${manifest}.apk`;
|
|
96
94
|
const androidJarPath = path.resolve(platformPath, 'android.jar');
|
|
@@ -102,37 +100,53 @@ export async function compileManifest (manifest, manifestPackage, targetPackage)
|
|
|
102
100
|
// https://developer.android.com/studio/command-line/aapt2
|
|
103
101
|
const args = [
|
|
104
102
|
'link',
|
|
105
|
-
'-o',
|
|
106
|
-
|
|
107
|
-
'--
|
|
108
|
-
|
|
109
|
-
'-
|
|
103
|
+
'-o',
|
|
104
|
+
resultPath,
|
|
105
|
+
'--manifest',
|
|
106
|
+
manifest,
|
|
107
|
+
'--rename-manifest-package',
|
|
108
|
+
manifestPackage,
|
|
109
|
+
'--rename-instrumentation-target-package',
|
|
110
|
+
targetPackage,
|
|
111
|
+
'-I',
|
|
112
|
+
androidJarPath,
|
|
110
113
|
'-v',
|
|
111
114
|
];
|
|
112
|
-
log.debug(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
log.debug(
|
|
116
|
+
`Compiling the manifest using '${util.quote([
|
|
117
|
+
/** @type {import('./types').StringRecord} */ (this.binaries).aapt2,
|
|
118
|
+
...args,
|
|
119
|
+
])}'`,
|
|
120
|
+
);
|
|
121
|
+
await exec(/** @type {import('./types').StringRecord} */ (this.binaries).aapt2, args);
|
|
117
122
|
} catch (e) {
|
|
118
|
-
log.debug(
|
|
119
|
-
|
|
123
|
+
log.debug(
|
|
124
|
+
'Cannot compile the manifest using aapt2. Defaulting to aapt. ' +
|
|
125
|
+
`Original error: ${e.stderr || e.message}`,
|
|
126
|
+
);
|
|
120
127
|
await this.initAapt();
|
|
121
128
|
const args = [
|
|
122
129
|
'package',
|
|
123
|
-
'-M',
|
|
124
|
-
|
|
125
|
-
'--rename-
|
|
126
|
-
|
|
127
|
-
'-
|
|
130
|
+
'-M',
|
|
131
|
+
manifest,
|
|
132
|
+
'--rename-manifest-package',
|
|
133
|
+
manifestPackage,
|
|
134
|
+
'--rename-instrumentation-target-package',
|
|
135
|
+
targetPackage,
|
|
136
|
+
'-I',
|
|
137
|
+
androidJarPath,
|
|
138
|
+
'-F',
|
|
139
|
+
resultPath,
|
|
128
140
|
'-f',
|
|
129
141
|
];
|
|
130
|
-
log.debug(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
log.debug(
|
|
143
|
+
`Compiling the manifest using '${util.quote([
|
|
144
|
+
/** @type {import('./types').StringRecord} */ (this.binaries).aapt,
|
|
145
|
+
...args,
|
|
146
|
+
])}'`,
|
|
147
|
+
);
|
|
134
148
|
try {
|
|
135
|
-
await exec(
|
|
149
|
+
await exec(/** @type {import('./types').StringRecord} */ (this.binaries).aapt, args);
|
|
136
150
|
} catch (e1) {
|
|
137
151
|
throw new Error(`Cannot compile the manifest. Original error: ${e1.stderr || e1.message}`);
|
|
138
152
|
}
|
|
@@ -154,7 +168,7 @@ export async function compileManifest (manifest, manifestPackage, targetPackage)
|
|
|
154
168
|
* @param {string} dstApk - Full path to the resulting package.
|
|
155
169
|
* The file will be overridden if it already exists.
|
|
156
170
|
*/
|
|
157
|
-
export async function insertManifest
|
|
171
|
+
export async function insertManifest(manifest, srcApk, dstApk) {
|
|
158
172
|
log.debug(`Inserting manifest '${manifest}', src: '${srcApk}', dst: '${dstApk}'`);
|
|
159
173
|
await zip.assertValidZip(srcApk);
|
|
160
174
|
await unzipFile(`${manifest}.apk`);
|
|
@@ -164,16 +178,22 @@ export async function insertManifest (manifest, srcApk, dstApk) {
|
|
|
164
178
|
await fs.copyFile(srcApk, dstApk);
|
|
165
179
|
log.debug('Moving manifest');
|
|
166
180
|
try {
|
|
167
|
-
await exec(
|
|
168
|
-
'remove',
|
|
181
|
+
await exec(/** @type {import('./types').StringRecord} */ (this.binaries).aapt, [
|
|
182
|
+
'remove',
|
|
183
|
+
dstApk,
|
|
184
|
+
manifestName,
|
|
169
185
|
]);
|
|
170
186
|
} catch {}
|
|
171
|
-
await exec(
|
|
172
|
-
'
|
|
173
|
-
|
|
187
|
+
await exec(
|
|
188
|
+
/** @type {import('./types').StringRecord} */ (this.binaries).aapt,
|
|
189
|
+
['add', dstApk, manifestName],
|
|
190
|
+
{cwd: path.dirname(manifest)},
|
|
191
|
+
);
|
|
174
192
|
} catch (e) {
|
|
175
|
-
log.debug(
|
|
176
|
-
|
|
193
|
+
log.debug(
|
|
194
|
+
'Cannot insert manifest using aapt. Defaulting to zip. ' +
|
|
195
|
+
`Original error: ${e.stderr || e.message}`,
|
|
196
|
+
);
|
|
177
197
|
const tmpRoot = await tempDir.openDir();
|
|
178
198
|
try {
|
|
179
199
|
// Unfortunately NodeJS does not provide any reliable methods
|
|
@@ -201,14 +221,16 @@ export async function insertManifest (manifest, srcApk, dstApk) {
|
|
|
201
221
|
* @param {string} appPath - The full path to .apk(s) package.
|
|
202
222
|
* @return {Promise<boolean>} True if the manifest requires Internet access permission.
|
|
203
223
|
*/
|
|
204
|
-
export async function hasInternetPermissionFromManifest
|
|
224
|
+
export async function hasInternetPermissionFromManifest(appPath) {
|
|
205
225
|
log.debug(`Checking if '${appPath}' requires internet access permission in the manifest`);
|
|
206
226
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
207
227
|
appPath = await this.extractBaseApk(appPath);
|
|
208
228
|
}
|
|
209
229
|
|
|
210
230
|
const {usesPermissions} = await readPackageManifest.bind(this)(appPath);
|
|
211
|
-
return usesPermissions.some(
|
|
231
|
+
return usesPermissions.some(
|
|
232
|
+
(/** @type {string} */ name) => name === 'android.permission.INTERNET',
|
|
233
|
+
);
|
|
212
234
|
}
|
|
213
235
|
|
|
214
236
|
// #region Private functions
|
|
@@ -219,7 +241,7 @@ export async function hasInternetPermissionFromManifest (appPath) {
|
|
|
219
241
|
* @param {string} sdkRoot
|
|
220
242
|
* @return {Promise<import('./types').PlatformInfo>} The resulting path to the newest installed platform.
|
|
221
243
|
*/
|
|
222
|
-
export async function getAndroidPlatformAndPath
|
|
244
|
+
export async function getAndroidPlatformAndPath(sdkRoot) {
|
|
223
245
|
const propsPaths = await fs.glob('*/build.prop', {
|
|
224
246
|
cwd: path.resolve(sdkRoot, 'platforms'),
|
|
225
247
|
absolute: true,
|
|
@@ -241,8 +263,10 @@ export async function getAndroidPlatformAndPath (sdkRoot) {
|
|
|
241
263
|
};
|
|
242
264
|
}
|
|
243
265
|
if (_.isEmpty(platformsMapping)) {
|
|
244
|
-
log.warn(
|
|
245
|
-
`
|
|
266
|
+
log.warn(
|
|
267
|
+
`Found zero platform folders at '${path.resolve(sdkRoot, 'platforms')}'. ` +
|
|
268
|
+
`Do you have any Android SDKs installed?`,
|
|
269
|
+
);
|
|
246
270
|
return {
|
|
247
271
|
platform: null,
|
|
248
272
|
platformPath: null,
|