appium-adb 14.0.6 → 14.0.7
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 +6 -0
- package/build/lib/tools/apk-signing.d.ts +34 -40
- package/build/lib/tools/apk-signing.d.ts.map +1 -1
- package/build/lib/tools/apk-signing.js +73 -71
- package/build/lib/tools/apk-signing.js.map +1 -1
- package/build/lib/tools/emulator-commands.d.ts +53 -69
- package/build/lib/tools/emulator-commands.d.ts.map +1 -1
- package/build/lib/tools/emulator-commands.js +48 -66
- package/build/lib/tools/emulator-commands.js.map +1 -1
- package/lib/tools/{apk-signing.js → apk-signing.ts} +96 -89
- package/lib/tools/{emulator-commands.js → emulator-commands.ts} +88 -92
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import _fs from 'fs';
|
|
3
|
-
import { exec } from 'teen_process';
|
|
3
|
+
import { exec, type ExecError } from 'teen_process';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { log } from '../logger.js';
|
|
6
6
|
import { tempDir, system, mkdirp, fs, util, zip } from '@appium/support';
|
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
APKS_EXTENSION,
|
|
12
12
|
getResourcePath,
|
|
13
13
|
} from '../helpers.js';
|
|
14
|
+
import type { ADB } from '../adb.js';
|
|
15
|
+
import type {
|
|
16
|
+
StringRecord,
|
|
17
|
+
SignedAppCacheValue,
|
|
18
|
+
CertCheckOptions,
|
|
19
|
+
KeystoreHash
|
|
20
|
+
} from './types.js';
|
|
14
21
|
|
|
15
22
|
const DEFAULT_PRIVATE_KEY = path.join('keys', 'testkey.pk8');
|
|
16
23
|
const DEFAULT_CERTIFICATE = path.join('keys', 'testkey.x509.pem');
|
|
@@ -20,25 +27,23 @@ const SHA1 = 'sha1';
|
|
|
20
27
|
const SHA256 = 'sha256';
|
|
21
28
|
const SHA512 = 'sha512';
|
|
22
29
|
const MD5 = 'md5';
|
|
23
|
-
const DEFAULT_CERT_HASH = {
|
|
30
|
+
const DEFAULT_CERT_HASH: KeystoreHash = {
|
|
24
31
|
[SHA256]: 'a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc'
|
|
25
32
|
};
|
|
26
33
|
const JAVA_PROPS_INIT_ERROR = 'java.lang.Error: Properties init';
|
|
27
|
-
|
|
28
|
-
const SIGNED_APPS_CACHE = new LRUCache({
|
|
34
|
+
const SIGNED_APPS_CACHE = new LRUCache<string, SignedAppCacheValue>({
|
|
29
35
|
max: 30,
|
|
30
36
|
});
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* Execute apksigner utility with given arguments.
|
|
34
40
|
*
|
|
35
|
-
* @
|
|
36
|
-
* @
|
|
37
|
-
* @
|
|
38
|
-
* @throws {Error} If apksigner binary is not present on the local file system
|
|
41
|
+
* @param args - The list of tool arguments.
|
|
42
|
+
* @returns - Command stdout
|
|
43
|
+
* @throws If apksigner binary is not present on the local file system
|
|
39
44
|
* or the return code is not equal to zero.
|
|
40
45
|
*/
|
|
41
|
-
export async function executeApksigner (args) {
|
|
46
|
+
export async function executeApksigner (this: ADB, args: string[]): Promise<string> {
|
|
42
47
|
const apkSignerJar = await getApksignerForOs.bind(this)();
|
|
43
48
|
const fullCmd = [
|
|
44
49
|
await getJavaForOs(), '-Xmx1024M', '-Xss1m',
|
|
@@ -52,18 +57,20 @@ export async function executeApksigner (args) {
|
|
|
52
57
|
// @ts-ignore This works
|
|
53
58
|
windowsVerbatimArguments: system.isWindows(),
|
|
54
59
|
});
|
|
55
|
-
for (
|
|
60
|
+
for (const [name, stream] of [['stdout', stdout], ['stderr', stderr]] as const) {
|
|
56
61
|
if (!_.trim(stream)) {
|
|
57
62
|
continue;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
if (name === 'stdout') {
|
|
61
66
|
// Make the output less talkative
|
|
62
|
-
|
|
67
|
+
const filteredStream = stream.split('\n')
|
|
63
68
|
.filter((line) => !line.includes('WARNING:'))
|
|
64
69
|
.join('\n');
|
|
70
|
+
log.debug(`apksigner ${name}: ${filteredStream}`);
|
|
71
|
+
} else {
|
|
72
|
+
log.debug(`apksigner ${name}: ${stream}`);
|
|
65
73
|
}
|
|
66
|
-
log.debug(`apksigner ${name}: ${stream}`);
|
|
67
74
|
}
|
|
68
75
|
return stdout;
|
|
69
76
|
}
|
|
@@ -71,11 +78,10 @@ export async function executeApksigner (args) {
|
|
|
71
78
|
/**
|
|
72
79
|
* (Re)sign the given apk file on the local file system with the default certificate.
|
|
73
80
|
*
|
|
74
|
-
* @
|
|
75
|
-
* @
|
|
76
|
-
* @throws {Error} If signing fails.
|
|
81
|
+
* @param apk - The full path to the local apk file.
|
|
82
|
+
* @throws If signing fails.
|
|
77
83
|
*/
|
|
78
|
-
export async function signWithDefaultCert (apk) {
|
|
84
|
+
export async function signWithDefaultCert (this: ADB, apk: string): Promise<void> {
|
|
79
85
|
log.debug(`Signing '${apk}' with default cert`);
|
|
80
86
|
if (!(await fs.exists(apk))) {
|
|
81
87
|
throw new Error(`${apk} file doesn't exist.`);
|
|
@@ -90,21 +96,21 @@ export async function signWithDefaultCert (apk) {
|
|
|
90
96
|
try {
|
|
91
97
|
await this.executeApksigner(args);
|
|
92
98
|
} catch (e) {
|
|
99
|
+
const err = e as ExecError;
|
|
93
100
|
throw new Error(`Could not sign '${apk}' with the default certificate. ` +
|
|
94
|
-
`Original error: ${
|
|
101
|
+
`Original error: ${err.stderr || err.stdout || err.message}`);
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
/**
|
|
99
106
|
* (Re)sign the given apk file on the local file system with a custom certificate.
|
|
100
107
|
*
|
|
101
|
-
* @
|
|
102
|
-
* @
|
|
103
|
-
* @throws {Error} If signing fails.
|
|
108
|
+
* @param apk - The full path to the local apk file.
|
|
109
|
+
* @throws If signing fails.
|
|
104
110
|
*/
|
|
105
|
-
export async function signWithCustomCert (apk) {
|
|
111
|
+
export async function signWithCustomCert (this: ADB, apk: string): Promise<void> {
|
|
106
112
|
log.debug(`Signing '${apk}' with custom cert`);
|
|
107
|
-
if (!(await fs.exists(
|
|
113
|
+
if (!(await fs.exists(this.keystorePath as string))) {
|
|
108
114
|
throw new Error(`Keystore: ${this.keystorePath} doesn't exist.`);
|
|
109
115
|
}
|
|
110
116
|
if (!(await fs.exists(apk))) {
|
|
@@ -113,14 +119,15 @@ export async function signWithCustomCert (apk) {
|
|
|
113
119
|
|
|
114
120
|
try {
|
|
115
121
|
await this.executeApksigner(['sign',
|
|
116
|
-
'--ks',
|
|
117
|
-
'--ks-key-alias',
|
|
122
|
+
'--ks', this.keystorePath as string,
|
|
123
|
+
'--ks-key-alias', this.keyAlias as string,
|
|
118
124
|
'--ks-pass', `pass:${this.keystorePassword}`,
|
|
119
125
|
'--key-pass', `pass:${this.keyPassword}`,
|
|
120
126
|
apk]);
|
|
121
127
|
} catch (err) {
|
|
128
|
+
const error = err as ExecError;
|
|
122
129
|
log.warn(`Cannot use apksigner tool for signing. Defaulting to jarsigner. ` +
|
|
123
|
-
`Original error: ${
|
|
130
|
+
`Original error: ${error.stderr || error.stdout || error.message}`);
|
|
124
131
|
try {
|
|
125
132
|
if (await unsignApk(apk)) {
|
|
126
133
|
log.debug(`'${apk}' has been successfully unsigned`);
|
|
@@ -129,22 +136,22 @@ export async function signWithCustomCert (apk) {
|
|
|
129
136
|
}
|
|
130
137
|
const jarsigner = path.resolve(await getJavaHome(), 'bin',
|
|
131
138
|
`jarsigner${system.isWindows() ? '.exe' : ''}`);
|
|
132
|
-
|
|
133
|
-
const fullCmd = [jarsigner,
|
|
139
|
+
const fullCmd: string[] = [jarsigner,
|
|
134
140
|
'-sigalg', 'MD5withRSA',
|
|
135
141
|
'-digestalg', 'SHA1',
|
|
136
|
-
'-keystore',
|
|
137
|
-
'-storepass',
|
|
138
|
-
'-keypass',
|
|
139
|
-
apk,
|
|
142
|
+
'-keystore', this.keystorePath as string,
|
|
143
|
+
'-storepass', this.keystorePassword as string,
|
|
144
|
+
'-keypass', this.keyPassword as string,
|
|
145
|
+
apk, this.keyAlias as string];
|
|
140
146
|
log.debug(`Starting jarsigner: ${util.quote(fullCmd)}`);
|
|
141
147
|
await exec(fullCmd[0], fullCmd.slice(1), {
|
|
142
148
|
// @ts-ignore This works
|
|
143
149
|
windowsVerbatimArguments: system.isWindows(),
|
|
144
150
|
});
|
|
145
151
|
} catch (e) {
|
|
152
|
+
const execErr = e as ExecError;
|
|
146
153
|
throw new Error(`Could not sign with custom certificate. ` +
|
|
147
|
-
`Original error: ${
|
|
154
|
+
`Original error: ${execErr.stderr || execErr.message}`);
|
|
148
155
|
}
|
|
149
156
|
}
|
|
150
157
|
}
|
|
@@ -154,11 +161,10 @@ export async function signWithCustomCert (apk) {
|
|
|
154
161
|
* custom or default certificate based on _this.useKeystore_ property value
|
|
155
162
|
* and Zip-aligns it after signing.
|
|
156
163
|
*
|
|
157
|
-
* @
|
|
158
|
-
* @
|
|
159
|
-
* @throws {Error} If signing fails.
|
|
164
|
+
* @param appPath - The full path to the local .apk(s) file.
|
|
165
|
+
* @throws If signing fails.
|
|
160
166
|
*/
|
|
161
|
-
export async function sign (appPath) {
|
|
167
|
+
export async function sign (this: ADB, appPath: string): Promise<void> {
|
|
162
168
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
163
169
|
let message = 'Signing of .apks-files is not supported. ';
|
|
164
170
|
if (this.useKeystore) {
|
|
@@ -186,16 +192,15 @@ export async function sign (appPath) {
|
|
|
186
192
|
/**
|
|
187
193
|
* Perform zip-aligning to the given local apk file.
|
|
188
194
|
*
|
|
189
|
-
* @
|
|
190
|
-
* @
|
|
191
|
-
* @returns {Promise<boolean>} True if the apk has been successfully aligned
|
|
195
|
+
* @param apk - The full path to the local apk file.
|
|
196
|
+
* @returns True if the apk has been successfully aligned
|
|
192
197
|
* or false if the apk has been already aligned.
|
|
193
|
-
* @throws
|
|
198
|
+
* @throws If zip-align fails.
|
|
194
199
|
*/
|
|
195
|
-
export async function zipAlignApk (apk) {
|
|
200
|
+
export async function zipAlignApk (this: ADB, apk: string): Promise<boolean> {
|
|
196
201
|
await this.initZipAlign();
|
|
197
202
|
try {
|
|
198
|
-
await exec((
|
|
203
|
+
await exec((this.binaries as StringRecord).zipalign as string, ['-c', '4', apk]);
|
|
199
204
|
log.debug(`${apk}' is already zip-aligned. Doing nothing`);
|
|
200
205
|
return false;
|
|
201
206
|
} catch {
|
|
@@ -212,43 +217,44 @@ export async function zipAlignApk (apk) {
|
|
|
212
217
|
await mkdirp(path.dirname(alignedApk));
|
|
213
218
|
try {
|
|
214
219
|
await exec(
|
|
215
|
-
(
|
|
220
|
+
(this.binaries as StringRecord).zipalign as string,
|
|
216
221
|
['-f', '4', apk, alignedApk]
|
|
217
222
|
);
|
|
218
223
|
await fs.mv(alignedApk, apk, { mkdirp: true });
|
|
219
224
|
return true;
|
|
220
225
|
} catch (e) {
|
|
226
|
+
const err = e as Error;
|
|
221
227
|
if (await fs.exists(alignedApk)) {
|
|
222
228
|
await fs.unlink(alignedApk);
|
|
223
229
|
}
|
|
224
|
-
throw new Error(`zipAlignApk failed. Original error: ${
|
|
230
|
+
throw new Error(`zipAlignApk failed. Original error: ${err.message || (err as ExecError).stderr}`);
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
|
|
228
234
|
/**
|
|
229
235
|
* Check if the app is already signed with the default Appium certificate.
|
|
230
236
|
*
|
|
231
|
-
* @
|
|
232
|
-
* @param
|
|
233
|
-
* @param
|
|
234
|
-
* @
|
|
235
|
-
* @return {Promise<boolean>} True if given application is already signed.
|
|
237
|
+
* @param appPath - The full path to the local .apk(s) file.
|
|
238
|
+
* @param pkg - The name of application package.
|
|
239
|
+
* @param opts - Certificate checking options
|
|
240
|
+
* @returns True if given application is already signed.
|
|
236
241
|
*/
|
|
237
|
-
export async function checkApkCert (appPath, pkg, opts = {}) {
|
|
242
|
+
export async function checkApkCert (this: ADB, appPath: string, pkg: string, opts: CertCheckOptions = {}): Promise<boolean> {
|
|
238
243
|
log.debug(`Checking app cert for ${appPath}`);
|
|
239
244
|
if (!await fs.exists(appPath)) {
|
|
240
245
|
log.debug(`'${appPath}' does not exist`);
|
|
241
246
|
return false;
|
|
242
247
|
}
|
|
243
248
|
|
|
249
|
+
let actualAppPath = appPath;
|
|
244
250
|
if (path.extname(appPath) === APKS_EXTENSION) {
|
|
245
|
-
|
|
251
|
+
actualAppPath = await this.extractBaseApk(appPath);
|
|
246
252
|
}
|
|
247
253
|
|
|
248
|
-
const hashMatches = (apksignerOutput, expectedHashes) => {
|
|
254
|
+
const hashMatches = (apksignerOutput: string, expectedHashes: KeystoreHash): boolean => {
|
|
249
255
|
for (const [name, value] of _.toPairs(expectedHashes)) {
|
|
250
|
-
if (new RegExp(`digest:\\s+${value}\\b`, 'i').test(apksignerOutput)) {
|
|
251
|
-
log.debug(`${name} hash did match for '${path.basename(
|
|
256
|
+
if (value && new RegExp(`digest:\\s+${value}\\b`, 'i').test(apksignerOutput)) {
|
|
257
|
+
log.debug(`${name} hash did match for '${path.basename(actualAppPath)}'`);
|
|
252
258
|
return true;
|
|
253
259
|
}
|
|
254
260
|
}
|
|
@@ -259,27 +265,28 @@ export async function checkApkCert (appPath, pkg, opts = {}) {
|
|
|
259
265
|
requireDefaultCert = true,
|
|
260
266
|
} = opts;
|
|
261
267
|
|
|
262
|
-
const appHash = await fs.hash(
|
|
268
|
+
const appHash = await fs.hash(actualAppPath);
|
|
263
269
|
if (SIGNED_APPS_CACHE.has(appHash)) {
|
|
264
|
-
log.debug(`Using the previously cached signature entry for '${path.basename(
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
log.debug(`Using the previously cached signature entry for '${path.basename(actualAppPath)}'`);
|
|
271
|
+
const cached = SIGNED_APPS_CACHE.get(appHash);
|
|
272
|
+
if (cached) {
|
|
273
|
+
const {keystorePath, output, expected} = cached;
|
|
274
|
+
if (this.useKeystore && this.keystorePath === keystorePath || !this.useKeystore) {
|
|
275
|
+
return (!this.useKeystore && !requireDefaultCert) || hashMatches(output, expected);
|
|
276
|
+
}
|
|
270
277
|
}
|
|
271
278
|
}
|
|
272
279
|
|
|
273
280
|
const expected = this.useKeystore ? await this.getKeystoreHash() : DEFAULT_CERT_HASH;
|
|
274
281
|
try {
|
|
275
282
|
await getApksignerForOs.bind(this)();
|
|
276
|
-
const output = await this.executeApksigner(['verify', '--print-certs',
|
|
283
|
+
const output = await this.executeApksigner(['verify', '--print-certs', actualAppPath]);
|
|
277
284
|
const hasMatch = hashMatches(output, expected);
|
|
278
285
|
if (hasMatch) {
|
|
279
|
-
log.info(`'${
|
|
286
|
+
log.info(`'${actualAppPath}' is signed with the ` +
|
|
280
287
|
`${this.useKeystore ? 'keystore' : 'default'} certificate`);
|
|
281
288
|
} else {
|
|
282
|
-
log.info(`'${
|
|
289
|
+
log.info(`'${actualAppPath}' is signed with a ` +
|
|
283
290
|
`non-${this.useKeystore ? 'keystore' : 'default'} certificate`);
|
|
284
291
|
}
|
|
285
292
|
const isSigned = (!this.useKeystore && !requireDefaultCert) || hasMatch;
|
|
@@ -287,17 +294,18 @@ export async function checkApkCert (appPath, pkg, opts = {}) {
|
|
|
287
294
|
SIGNED_APPS_CACHE.set(appHash, {
|
|
288
295
|
output,
|
|
289
296
|
expected,
|
|
290
|
-
keystorePath:
|
|
297
|
+
keystorePath: this.keystorePath as string,
|
|
291
298
|
});
|
|
292
299
|
}
|
|
293
300
|
return isSigned;
|
|
294
301
|
} catch (err) {
|
|
302
|
+
const error = err as ExecError;
|
|
295
303
|
// check if there is no signature
|
|
296
|
-
if (_.includes(
|
|
297
|
-
log.info(`'${
|
|
304
|
+
if (_.includes(error.stderr, APKSIGNER_VERIFY_FAIL)) {
|
|
305
|
+
log.info(`'${actualAppPath}' is not signed`);
|
|
298
306
|
return false;
|
|
299
307
|
}
|
|
300
|
-
const errMsg =
|
|
308
|
+
const errMsg = error.stderr || error.stdout || error.message;
|
|
301
309
|
if (_.includes(errMsg, JAVA_PROPS_INIT_ERROR)) {
|
|
302
310
|
// This error pops up randomly and we are not quite sure why.
|
|
303
311
|
// My guess - a race condition in java vm initialization.
|
|
@@ -308,10 +316,10 @@ export async function checkApkCert (appPath, pkg, opts = {}) {
|
|
|
308
316
|
// would anyway fail.
|
|
309
317
|
// See https://github.com/appium/appium/issues/14724 for more details.
|
|
310
318
|
log.warn(errMsg);
|
|
311
|
-
log.warn(`Assuming '${
|
|
319
|
+
log.warn(`Assuming '${actualAppPath}' is already signed and continuing anyway`);
|
|
312
320
|
return true;
|
|
313
321
|
}
|
|
314
|
-
throw new Error(`Cannot verify the signature of '${
|
|
322
|
+
throw new Error(`Cannot verify the signature of '${actualAppPath}'. ` +
|
|
315
323
|
`Original error: ${errMsg}`);
|
|
316
324
|
}
|
|
317
325
|
}
|
|
@@ -319,23 +327,21 @@ export async function checkApkCert (appPath, pkg, opts = {}) {
|
|
|
319
327
|
/**
|
|
320
328
|
* Retrieve the the hash of the given keystore.
|
|
321
329
|
*
|
|
322
|
-
* @
|
|
323
|
-
* @
|
|
324
|
-
* @throws {Error} If getting keystore hash fails.
|
|
330
|
+
* @returns
|
|
331
|
+
* @throws If getting keystore hash fails.
|
|
325
332
|
*/
|
|
326
|
-
export async function getKeystoreHash () {
|
|
333
|
+
export async function getKeystoreHash (this: ADB): Promise<KeystoreHash> {
|
|
327
334
|
log.debug(`Getting hash of the '${this.keystorePath}' keystore`);
|
|
328
335
|
const keytool = path.resolve(await getJavaHome(), 'bin',
|
|
329
336
|
`keytool${system.isWindows() ? '.exe' : ''}`);
|
|
330
337
|
if (!await fs.exists(keytool)) {
|
|
331
338
|
throw new Error(`The keytool utility cannot be found at '${keytool}'`);
|
|
332
339
|
}
|
|
333
|
-
|
|
334
|
-
const args = [
|
|
340
|
+
const args: string[] = [
|
|
335
341
|
'-v', '-list',
|
|
336
|
-
'-alias',
|
|
337
|
-
'-keystore',
|
|
338
|
-
'-storepass',
|
|
342
|
+
'-alias', this.keyAlias as string,
|
|
343
|
+
'-keystore', this.keystorePath as string,
|
|
344
|
+
'-storepass', this.keystorePassword as string,
|
|
339
345
|
];
|
|
340
346
|
log.info(`Running '${keytool}' with arguments: ${util.quote(args)}`);
|
|
341
347
|
try {
|
|
@@ -343,7 +349,7 @@ export async function getKeystoreHash () {
|
|
|
343
349
|
// @ts-ignore This property is ok
|
|
344
350
|
windowsVerbatimArguments: system.isWindows(),
|
|
345
351
|
});
|
|
346
|
-
const result = {};
|
|
352
|
+
const result: KeystoreHash = {};
|
|
347
353
|
for (const hashName of [SHA512, SHA256, SHA1, MD5]) {
|
|
348
354
|
const hashRe = new RegExp(`^\\s*${hashName}:\\s*([a-f0-9:]+)`, 'mi');
|
|
349
355
|
const match = hashRe.exec(stdout);
|
|
@@ -359,8 +365,9 @@ export async function getKeystoreHash () {
|
|
|
359
365
|
log.debug(`Keystore hash: ${JSON.stringify(result)}`);
|
|
360
366
|
return result;
|
|
361
367
|
} catch (e) {
|
|
368
|
+
const err = e as ExecError;
|
|
362
369
|
throw new Error(`Cannot get the hash of '${this.keystorePath}' keystore. ` +
|
|
363
|
-
`Original error: ${
|
|
370
|
+
`Original error: ${err.stderr || err.message}`);
|
|
364
371
|
}
|
|
365
372
|
}
|
|
366
373
|
|
|
@@ -369,11 +376,10 @@ export async function getKeystoreHash () {
|
|
|
369
376
|
/**
|
|
370
377
|
* Get the absolute path to apksigner tool
|
|
371
378
|
*
|
|
372
|
-
* @
|
|
373
|
-
* @
|
|
374
|
-
* @throws {Error} If the tool is not present on the local file system.
|
|
379
|
+
* @returns An absolute path to apksigner tool.
|
|
380
|
+
* @throws If the tool is not present on the local file system.
|
|
375
381
|
*/
|
|
376
|
-
export async function getApksignerForOs () {
|
|
382
|
+
export async function getApksignerForOs (this: ADB): Promise<string> {
|
|
377
383
|
return await this.getBinaryFromSdkRoot('apksigner.jar');
|
|
378
384
|
}
|
|
379
385
|
|
|
@@ -382,12 +388,12 @@ export async function getApksignerForOs () {
|
|
|
382
388
|
* META-INF folder recursively from the archive.
|
|
383
389
|
* !!! The function overwrites the given apk after successful unsigning !!!
|
|
384
390
|
*
|
|
385
|
-
* @param
|
|
386
|
-
* @returns
|
|
391
|
+
* @param apkPath The path to the apk
|
|
392
|
+
* @returns `true` if the apk has been successfully
|
|
387
393
|
* unsigned and overwritten
|
|
388
|
-
* @throws
|
|
394
|
+
* @throws if there was an error during the unsign operation
|
|
389
395
|
*/
|
|
390
|
-
export async function unsignApk (apkPath) {
|
|
396
|
+
export async function unsignApk (apkPath: string): Promise<boolean> {
|
|
391
397
|
const tmpRoot = await tempDir.openDir();
|
|
392
398
|
const metaInfFolderName = 'META-INF';
|
|
393
399
|
try {
|
|
@@ -416,3 +422,4 @@ export async function unsignApk (apkPath) {
|
|
|
416
422
|
}
|
|
417
423
|
|
|
418
424
|
// #endregion
|
|
425
|
+
|