UNPKG

appium-adb

Version:

Android Debug Bridge interface

250 lines 10.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.packageAndLaunchActivityFromManifest = packageAndLaunchActivityFromManifest; exports.targetSdkVersionFromManifest = targetSdkVersionFromManifest; exports.targetSdkVersionUsingPKG = targetSdkVersionUsingPKG; exports.compileManifest = compileManifest; exports.insertManifest = insertManifest; exports.hasInternetPermissionFromManifest = hasInternetPermissionFromManifest; exports.getAndroidPlatformAndPath = getAndroidPlatformAndPath; const lodash_1 = __importDefault(require("lodash")); const teen_process_1 = require("teen_process"); const logger_1 = require("../logger"); const helpers_1 = require("../helpers"); const support_1 = require("@appium/support"); const node_path_1 = __importDefault(require("node:path")); /** * Extract package and main activity name from application manifest. * * @param appPath - The full path to application .apk(s) package * @return The parsed application info. * @throws {error} If there was an error while getting the data from the given * application package. */ async function packageAndLaunchActivityFromManifest(appPath) { if (appPath.endsWith(helpers_1.APKS_EXTENSION)) { appPath = await this.extractBaseApk(appPath); } const { name: apkPackage, launchableActivity: { name: apkActivity }, } = await helpers_1.readPackageManifest.bind(this)(appPath); logger_1.log.info(`Package name: '${apkPackage}'`); logger_1.log.info(`Main activity name: '${apkActivity}'`); return { apkPackage, apkActivity }; } /** * Extract target SDK version from application manifest. * * @param appPath - The full path to .apk(s) package. * @return The version of the target SDK. * @throws {error} If there was an error while getting the data from the given * application package. */ async function targetSdkVersionFromManifest(appPath) { logger_1.log.debug(`Extracting target SDK version of '${appPath}'`); const originalAppPath = appPath; if (appPath.endsWith(helpers_1.APKS_EXTENSION)) { appPath = await this.extractBaseApk(appPath); } const { targetSdkVersion } = await helpers_1.readPackageManifest.bind(this)(appPath); if (!targetSdkVersion) { throw new Error(`Cannot extract targetSdkVersion of '${originalAppPath}'. Does ` + `the package manifest define it?`); } return targetSdkVersion; } /** * Extract target SDK version from package information. * * @param pkg - The class name of the package installed on the device under test. * @param cmdOutput - Optional parameter containing the output of * _dumpsys package_ command. It may speed up the method execution. * @return The version of the target SDK. */ async function targetSdkVersionUsingPKG(pkg, cmdOutput = null) { const stdout = cmdOutput || (await this.shell(['dumpsys', 'package', pkg])); const targetSdkVersionMatch = new RegExp(/targetSdk=([^\s\s]+)/g).exec(stdout); return targetSdkVersionMatch && targetSdkVersionMatch.length >= 2 ? parseInt(targetSdkVersionMatch[1], 10) : 0; } /** * Create binary representation of package manifest (usually AndroidManifest.xml). * `${manifest}.apk` file will be created as the result of this method * containing the compiled manifest. * * @param manifest - Full path to the initial manifest template * @param manifestPackage - The name of the manifest package * @param targetPackage - The name of the destination package */ async function compileManifest(manifest, manifestPackage, targetPackage) { const { platform, platformPath } = await getAndroidPlatformAndPath(this.sdkRoot); if (!platform || !platformPath) { throw new Error('Cannot compile the manifest. The required platform does not exist (API level >= 17)'); } const resultPath = `${manifest}.apk`; const androidJarPath = node_path_1.default.resolve(platformPath, 'android.jar'); if (await support_1.fs.exists(resultPath)) { await support_1.fs.rimraf(resultPath); } try { await this.initAapt2(); // https://developer.android.com/studio/command-line/aapt2 const binaries = this.binaries; const args = [ 'link', '-o', resultPath, '--manifest', manifest, '--rename-manifest-package', manifestPackage, '--rename-instrumentation-target-package', targetPackage, '-I', androidJarPath, '-v', ]; logger_1.log.debug(`Compiling the manifest using '${support_1.util.quote([binaries.aapt2, ...args])}'`); await (0, teen_process_1.exec)(binaries.aapt2, args); } catch (e) { logger_1.log.debug('Cannot compile the manifest using aapt2. Defaulting to aapt. ' + `Original error: ${e.message || e.stderr}`); await this.initAapt(); const binaries = this.binaries; const args = [ 'package', '-M', manifest, '--rename-manifest-package', manifestPackage, '--rename-instrumentation-target-package', targetPackage, '-I', androidJarPath, '-F', resultPath, '-f', ]; logger_1.log.debug(`Compiling the manifest using '${support_1.util.quote([binaries.aapt, ...args])}'`); try { await (0, teen_process_1.exec)(binaries.aapt, args); } catch (e1) { throw new Error(`Cannot compile the manifest. Original error: ${e1.message || e1.stderr}`); } } logger_1.log.debug(`Compiled the manifest at '${resultPath}'`); } /** * Replace/insert the specially precompiled manifest file into the * particular package. * * @param manifest - Full path to the precompiled manifest * created by `compileManifest` method call * without .apk extension * @param srcApk - Full path to the existing valid application package, where * this manifest has to be insetred to. This package * will NOT be modified. * @param dstApk - Full path to the resulting package. * The file will be overridden if it already exists. */ async function insertManifest(manifest, srcApk, dstApk) { logger_1.log.debug(`Inserting manifest '${manifest}', src: '${srcApk}', dst: '${dstApk}'`); await support_1.zip.assertValidZip(srcApk); await (0, helpers_1.unzipFile)(`${manifest}.apk`); const manifestName = node_path_1.default.basename(manifest); try { await this.initAapt(); const binaries = this.binaries; await support_1.fs.copyFile(srcApk, dstApk); logger_1.log.debug('Moving manifest'); try { await (0, teen_process_1.exec)(binaries.aapt, ['remove', dstApk, manifestName]); } catch { } await (0, teen_process_1.exec)(binaries.aapt, ['add', dstApk, manifestName], { cwd: node_path_1.default.dirname(manifest), }); } catch (e) { logger_1.log.debug('Cannot insert manifest using aapt. Defaulting to zip. ' + `Original error: ${e.message || e.stderr}`); const tmpRoot = await support_1.tempDir.openDir(); try { // Unfortunately NodeJS does not provide any reliable methods // to replace files inside zip archives without loading the // whole archive content into RAM logger_1.log.debug(`Extracting the source apk at '${srcApk}'`); await support_1.zip.extractAllTo(srcApk, tmpRoot); logger_1.log.debug('Moving manifest'); await support_1.fs.mv(manifest, node_path_1.default.resolve(tmpRoot, manifestName)); logger_1.log.debug(`Collecting the destination apk at '${dstApk}'`); await support_1.zip.toArchive(dstApk, { cwd: tmpRoot, }); } finally { await support_1.fs.rimraf(tmpRoot); } } logger_1.log.debug(`Manifest insertion into '${dstApk}' is completed`); } /** * Check whether package manifest contains Internet permissions. * * @param appPath - The full path to .apk(s) package. * @return True if the manifest requires Internet access permission. */ async function hasInternetPermissionFromManifest(appPath) { logger_1.log.debug(`Checking if '${appPath}' requires internet access permission in the manifest`); if (appPath.endsWith(helpers_1.APKS_EXTENSION)) { appPath = await this.extractBaseApk(appPath); } const { usesPermissions } = await helpers_1.readPackageManifest.bind(this)(appPath); return usesPermissions.some((name) => name === 'android.permission.INTERNET'); } // #region Private functions /** * Retrieve the path to the recent installed Android platform. * * @param sdkRoot * @return The resulting path to the newest installed platform. */ async function getAndroidPlatformAndPath(sdkRoot) { const propsPaths = await support_1.fs.glob('*/build.prop', { cwd: node_path_1.default.resolve(sdkRoot, 'platforms'), absolute: true, }); const platformsMapping = {}; for (const propsPath of propsPaths) { const propsContent = await support_1.fs.readFile(propsPath, 'utf-8'); const platformPath = node_path_1.default.dirname(propsPath); const platform = node_path_1.default.basename(platformPath); const match = /ro\.build\.version\.sdk=(\d+)/.exec(propsContent); if (!match) { logger_1.log.warn(`Cannot read the SDK version from '${propsPath}'. Skipping '${platform}'`); continue; } platformsMapping[parseInt(match[1], 10)] = { platform, platformPath, }; } if (lodash_1.default.isEmpty(platformsMapping)) { logger_1.log.warn(`Found zero platform folders at '${node_path_1.default.resolve(sdkRoot, 'platforms')}'. ` + `Do you have any Android SDKs installed?`); return { platform: null, platformPath: null, }; } const recentSdkVersion = lodash_1.default.keys(platformsMapping).sort().reverse()[0]; const result = platformsMapping[recentSdkVersion]; logger_1.log.debug(`Found the most recent Android platform: ${JSON.stringify(result)}`); return result; } // #endregion //# sourceMappingURL=android-manifest.js.map