diff --git a/lib/uiautomator2.js b/lib/uiautomator2.js index 5b06f97a9..0cc3b22f2 100644 --- a/lib/uiautomator2.js +++ b/lib/uiautomator2.js @@ -6,7 +6,7 @@ import { TEST_APK_PATH as testApkPath, version as serverVersion } from 'appium-uiautomator2-server'; -import { util, logger, timing } from 'appium/support'; +import { util, timing } from 'appium/support'; import B from 'bluebird'; import axios from 'axios'; @@ -17,7 +17,6 @@ const SERVICES_LAUNCH_TIMEOUT = 30000; const SERVER_PACKAGE_ID = 'io.appium.uiautomator2.server'; const SERVER_TEST_PACKAGE_ID = `${SERVER_PACKAGE_ID}.test`; const INSTRUMENTATION_TARGET = `${SERVER_TEST_PACKAGE_ID}/androidx.test.runner.AndroidJUnitRunner`; -const instrumentationLogger = logger.getLogger('Instrumentation'); class UIA2Proxy extends JWProxy { /** @type {boolean} */ @@ -50,6 +49,9 @@ class UiAutomator2Server { /** @type {boolean|undefined} */ disableSuppressAccessibilityService; + /** @type {import('teen_process').SubProcess|null} */ + instrumentationProcess; + constructor (log, opts = {}) { for (let req of REQD_PARAMS) { if (!opts || !util.hasValue(opts[req])) { @@ -72,6 +74,7 @@ class UiAutomator2Server { this.proxyReqRes = this.jwproxy.proxyReqRes.bind(this.jwproxy); this.proxyCommand = this.jwproxy.command.bind(this.jwproxy); this.jwproxy.didInstrumentationExit = false; + this.instrumentationProcess = null; } /** @@ -255,6 +258,9 @@ class UiAutomator2Server { while (retries < maxRetries) { this.log.info(`Waiting up to ${timeout}ms for UiAutomator2 to be online...`); this.jwproxy.didInstrumentationExit = false; + try { + await this.stopInstrumentationProcess(); + } catch (ign) {} await this.startInstrumentationProcess(); if (!this.jwproxy.didInstrumentationExit) { try { @@ -312,18 +318,30 @@ class UiAutomator2Server { // Disable Google analytics to prevent possible fatal exception cmd.push('-e', 'disableAnalytics', 'true'); cmd.push(INSTRUMENTATION_TARGET); - const instrumentationProcess = this.adb.createSubProcess(['shell', ...cmd]); - instrumentationProcess.on('output', (stdout, stderr) => { - const output = _.trim(stdout || stderr); - if (output) { - instrumentationLogger.debug(output); - } - }); - instrumentationProcess.on('exit', (code) => { - instrumentationLogger.debug(`The process has exited with code ${code}`); + this.instrumentationProcess = this.adb.createSubProcess(['shell', ...cmd]); + for (const streamName of ['stderr', 'stdout']) { + this.instrumentationProcess.on(`line-${streamName}`, (line) => this.log.debug(`[Instrumentation] ${line}`)); + } + this.instrumentationProcess.once('exit', (code, signal) => { + this.log.debug(`[Instrumentation] The process has exited with code ${code}, signal ${signal}`); this.jwproxy.didInstrumentationExit = true; }); - await instrumentationProcess.start(0); + await this.instrumentationProcess.start(0); + } + + async stopInstrumentationProcess () { + if (!this.instrumentationProcess) { + return; + } + + try { + if (this.instrumentationProcess.isRunning) { + await this.instrumentationProcess.stop(); + } + } finally { + this.instrumentationProcess.removeAllListeners(); + this.instrumentationProcess = null; + } } async deleteSession () { @@ -336,6 +354,11 @@ class UiAutomator2Server { this.log.warn(`Did not get confirmation UiAutomator2 deleteSession worked; ` + `Error was: ${err}`); } + try { + await this.stopInstrumentationProcess(); + } catch (err) { + this.log.warn(`Could not stop the instrumentation process. Original error: ${err.message}`); + } } async cleanupAutomationLeftovers (strictCleanup = false) { diff --git a/package.json b/package.json index 92575873b..e7f62ea33 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "singleQuote": true }, "dependencies": { - "appium-adb": "^12.2.0", + "appium-adb": "^12.4.7", "appium-android-driver": "^9.7.0", "appium-chromedriver": "^5.6.28", "appium-uiautomator2-server": "^7.0.14", @@ -68,7 +68,7 @@ "lodash": "^4.17.4", "portscanner": "^2.2.0", "source-map-support": "^0.x", - "teen_process": "^2.0.0", + "teen_process": "^2.2.0", "type-fest": "^4.4.0" }, "devDependencies": {