Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions lib/device/real-device-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {AfcClient} from './afc-client';
import {InstallationProxyClient} from './installation-proxy-client';
import {NotificationClient} from './notification-client';
import {isIos18OrNewer} from '../utils';
import {getRemoteXPCServices} from './remotexpc-utils';

const DEFAULT_APP_INSTALLATION_TIMEOUT_MS = 8 * 60 * 1000;
export const IO_TIMEOUT_MS = 4 * 60 * 1000;
Expand Down Expand Up @@ -433,6 +434,36 @@ export class RealDevice {
}

async terminateApp(bundleId: string, platformVersion: string): Promise<boolean> {
// For iOS 18 and above, use RemoteXPC DVT service.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is too complicated now. consider refactoring it into smaller private methods

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repo has a similar implementation, so I think such a refactor could be a follow-up PR with several places together. Just an option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we could do this in a separate PR

// processControl.getPidForBundleIdentifier gives us the PID directly from the bundle ID
if (isIos18OrNewer(this.driverOpts)) {
let remoteXPCConnection;
try {
const Services = await getRemoteXPCServices();
const dvt = await Services.startDVTService(this.udid);
remoteXPCConnection = dvt.remoteXPC;

const pid = await dvt.processControl.getPidForBundleIdentifier(bundleId);
if (!pid) {
this.log.info(`The process of the bundle id '${bundleId}' was not running`);
return false;
}
this.log.debug(`Found process for '${bundleId}' with PID ${pid} via RemoteXPC`);
await dvt.processControl.kill(pid);
return true;
} catch (err: any) {
this.log.error(`Failed to terminate '${bundleId}' via RemoteXPC: ${err.message}`);
// Fall through to devicectl/legacy path
} finally {
if (remoteXPCConnection) {
this.log.info(`Closing remoteXPC connection for device ${this.udid}`);
await remoteXPCConnection.close();
}
}
}

// Fallback for iOS 18+ (if RemoteXPC failed) and primary path for iOS 17.x
// For iOS < 17, use the legacy instrument service.
let instrumentService: any;
let installProxyClient: InstallationProxyClient | undefined;
try {
Expand All @@ -449,7 +480,7 @@ export class RealDevice {
this.log.debug(`The executable name for the bundle id '${bundleId}' was '${executableName}'`);

// 'devicectl' has overhead (generally?) than the instrument service via appium-ios-device,
// so hre uses the 'devicectl' only for iOS 17+.
// so it uses 'devicectl' only for iOS 17+.
if (util.compareVersions(platformVersion, '>=', '17.0')) {
this.log.debug(`Calling devicectl to kill the process`);

Expand All @@ -476,7 +507,7 @@ export class RealDevice {
this.log.info(`The process of the bundle id '${bundleId}' was not running`);
return false;
}
await instrumentService.callChannel(
await instrumentService.callChannel(
INSTRUMENT_CHANNEL.PROCESS_CONTROL,
'killPid:',
`${process.pid}`,
Expand Down
Loading