appium-ios-remotexpc 0.18.0 → 0.19.0
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/src/lib/apple-tv/encryption/ed25519.d.ts.map +1 -1
- package/build/src/lib/apple-tv/encryption/ed25519.js +1 -1
- package/build/src/lib/tunnel/packet-stream-client.js +1 -1
- package/build/src/lib/types.d.ts +46 -0
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/services/ios/dvt/channel.d.ts +5 -0
- package/build/src/services/ios/dvt/channel.d.ts.map +1 -1
- package/build/src/services/ios/dvt/channel.js +7 -0
- package/build/src/services/ios/dvt/instruments/application-listing.d.ts +2 -9
- package/build/src/services/ios/dvt/instruments/application-listing.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/application-listing.js +2 -15
- package/build/src/services/ios/dvt/instruments/base-instrument.d.ts +19 -0
- package/build/src/services/ios/dvt/instruments/base-instrument.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/base-instrument.js +24 -0
- package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts +2 -9
- package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/condition-inducer.js +2 -15
- package/build/src/services/ios/dvt/instruments/device-info.d.ts +2 -9
- package/build/src/services/ios/dvt/instruments/device-info.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/device-info.js +2 -14
- package/build/src/services/ios/dvt/instruments/graphics.d.ts +2 -6
- package/build/src/services/ios/dvt/instruments/graphics.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/graphics.js +2 -11
- package/build/src/services/ios/dvt/instruments/location-simulation.d.ts +2 -9
- package/build/src/services/ios/dvt/instruments/location-simulation.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/location-simulation.js +2 -15
- package/build/src/services/ios/dvt/instruments/notifications.d.ts +85 -0
- package/build/src/services/ios/dvt/instruments/notifications.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/notifications.js +68 -0
- package/build/src/services/ios/dvt/instruments/screenshot.d.ts +2 -6
- package/build/src/services/ios/dvt/instruments/screenshot.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/screenshot.js +2 -11
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +3 -0
- package/package.json +5 -2
- package/src/lib/apple-tv/encryption/ed25519.ts +2 -6
- package/src/lib/tunnel/packet-stream-client.ts +1 -1
- package/src/lib/types.ts +47 -0
- package/src/services/ios/dvt/channel.ts +8 -0
- package/src/services/ios/dvt/instruments/application-listing.ts +2 -17
- package/src/services/ios/dvt/instruments/base-instrument.ts +27 -0
- package/src/services/ios/dvt/instruments/condition-inducer.ts +2 -17
- package/src/services/ios/dvt/instruments/device-info.ts +2 -15
- package/src/services/ios/dvt/instruments/graphics.ts +2 -13
- package/src/services/ios/dvt/instruments/location-simulation.ts +2 -18
- package/src/services/ios/dvt/instruments/notifications.ts +144 -0
- package/src/services/ios/dvt/instruments/screenshot.ts +2 -13
- package/src/services.ts +3 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
+
import { MessageAux } from '../dtx-message.js';
|
|
3
|
+
import { decodeNSKeyedArchiver } from '../nskeyedarchiver-decoder.js';
|
|
4
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
5
|
+
const log = getLogger('Notifications');
|
|
6
|
+
/**
|
|
7
|
+
* Notifications service for monitoring iOS system events
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* for await (const msg of dvtService.notifications.messages()) {
|
|
12
|
+
* if (!msg) continue;
|
|
13
|
+
*
|
|
14
|
+
* if (msg.selector === 'applicationStateNotification:') {
|
|
15
|
+
* const app = msg.data;
|
|
16
|
+
* console.log(`${app.appName}: ${app.state_description}`);
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export class Notifications extends BaseInstrument {
|
|
22
|
+
/** DTX service identifier for mobile notifications */
|
|
23
|
+
static IDENTIFIER = 'com.apple.instruments.server.services.mobilenotifications';
|
|
24
|
+
async start() {
|
|
25
|
+
await this.initialize();
|
|
26
|
+
const args = new MessageAux().appendObj(true);
|
|
27
|
+
await this.channel.call('setApplicationStateNotificationsEnabled_')(args);
|
|
28
|
+
await this.channel.call('setMemoryNotificationsEnabled_')(args);
|
|
29
|
+
}
|
|
30
|
+
async stop() {
|
|
31
|
+
const args = new MessageAux().appendObj(false);
|
|
32
|
+
await this.channel.call('setApplicationStateNotificationsEnabled_')(args);
|
|
33
|
+
await this.channel.call('setMemoryNotificationsEnabled_')(args);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Yields notification messages from the iOS device
|
|
37
|
+
*/
|
|
38
|
+
async *messages() {
|
|
39
|
+
log.debug('Network monitoring has started');
|
|
40
|
+
await this.start();
|
|
41
|
+
try {
|
|
42
|
+
while (true) {
|
|
43
|
+
const [selector, auxiliaries] = await this.channel.receivePlistWithAux();
|
|
44
|
+
// Decode NSKeyedArchiver format in auxiliaries first index
|
|
45
|
+
const decodedData = auxiliaries[0];
|
|
46
|
+
if (decodedData &&
|
|
47
|
+
typeof decodedData === 'object' &&
|
|
48
|
+
decodedData.$archiver === 'NSKeyedArchiver') {
|
|
49
|
+
try {
|
|
50
|
+
const data = decodeNSKeyedArchiver(decodedData);
|
|
51
|
+
yield {
|
|
52
|
+
selector: selector,
|
|
53
|
+
data,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
log.warn('Failed to decode NSKeyedArchiver data:', error);
|
|
58
|
+
await this.stop();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
log.debug('Network monitoring has ended');
|
|
65
|
+
await this.stop();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
2
2
|
/**
|
|
3
3
|
* Screenshot service for capturing device screenshots
|
|
4
4
|
*/
|
|
5
|
-
export declare class Screenshot {
|
|
6
|
-
private readonly dvt;
|
|
5
|
+
export declare class Screenshot extends BaseInstrument {
|
|
7
6
|
static readonly IDENTIFIER = "com.apple.instruments.server.services.screenshot";
|
|
8
|
-
private channel;
|
|
9
|
-
constructor(dvt: DVTSecureSocketProxyService);
|
|
10
|
-
initialize(): Promise<void>;
|
|
11
7
|
/**
|
|
12
8
|
* Capture a screenshot from the device
|
|
13
9
|
* @returns The screenshot data as a Buffer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/dvt/instruments/screenshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/dvt/instruments/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD;;GAEG;AACH,qBAAa,UAAW,SAAQ,cAAc;IAC5C,MAAM,CAAC,QAAQ,CAAC,UAAU,sDAC2B;IAErD;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;CAmBvC"}
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
2
3
|
const log = getLogger('Screenshot');
|
|
3
4
|
/**
|
|
4
5
|
* Screenshot service for capturing device screenshots
|
|
5
6
|
*/
|
|
6
|
-
export class Screenshot {
|
|
7
|
-
dvt;
|
|
7
|
+
export class Screenshot extends BaseInstrument {
|
|
8
8
|
static IDENTIFIER = 'com.apple.instruments.server.services.screenshot';
|
|
9
|
-
channel = null;
|
|
10
|
-
constructor(dvt) {
|
|
11
|
-
this.dvt = dvt;
|
|
12
|
-
}
|
|
13
|
-
async initialize() {
|
|
14
|
-
if (!this.channel) {
|
|
15
|
-
this.channel = await this.dvt.makeChannel(Screenshot.IDENTIFIER);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
9
|
/**
|
|
19
10
|
* Capture a screenshot from the device
|
|
20
11
|
* @returns The screenshot data as a Buffer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;AAsBrD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,sCAAsC,CAAC,CAYjD;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uCAAuC,CAAC,CAYlD;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,6BAA6B,CAAC,CAYxC;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mCAAmC,CAAC,CAY9C;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,wBAAwB,CAAC,CAmCnC;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
|
package/build/src/services.js
CHANGED
|
@@ -10,6 +10,7 @@ import { ConditionInducer } from './services/ios/dvt/instruments/condition-induc
|
|
|
10
10
|
import { DeviceInfo } from './services/ios/dvt/instruments/device-info.js';
|
|
11
11
|
import { Graphics } from './services/ios/dvt/instruments/graphics.js';
|
|
12
12
|
import { LocationSimulation } from './services/ios/dvt/instruments/location-simulation.js';
|
|
13
|
+
import { Notifications } from './services/ios/dvt/instruments/notifications.js';
|
|
13
14
|
import { Screenshot } from './services/ios/dvt/instruments/screenshot.js';
|
|
14
15
|
import { MisagentService } from './services/ios/misagent/index.js';
|
|
15
16
|
import { MobileConfigService } from './services/ios/mobile-config/index.js';
|
|
@@ -142,6 +143,7 @@ export async function startDVTService(udid) {
|
|
|
142
143
|
const appListing = new ApplicationListing(dvtService);
|
|
143
144
|
const graphics = new Graphics(dvtService);
|
|
144
145
|
const deviceInfo = new DeviceInfo(dvtService);
|
|
146
|
+
const notification = new Notifications(dvtService);
|
|
145
147
|
return {
|
|
146
148
|
remoteXPC: remoteXPC,
|
|
147
149
|
dvtService,
|
|
@@ -151,6 +153,7 @@ export async function startDVTService(udid) {
|
|
|
151
153
|
appListing,
|
|
152
154
|
graphics,
|
|
153
155
|
deviceInfo,
|
|
156
|
+
notification,
|
|
154
157
|
};
|
|
155
158
|
}
|
|
156
159
|
export async function createRemoteXPCConnection(udid) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-ios-remotexpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"main": "build/src/index.js",
|
|
5
5
|
"types": "build/src/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"test:dvt:screenshot": "mocha test/integration/dvt_instruments/screenshot-test.ts --exit --timeout 1m",
|
|
47
47
|
"test:dvt:device-info": "mocha test/integration/dvt_instruments/device-info-test.ts --exit --timeout 1m",
|
|
48
48
|
"test:dvt:applist": "mocha test/integration/dvt_instruments/app-listing-test.ts --exit --timeout 1m",
|
|
49
|
+
"test:dvt:notification": "mocha test/integration/dvt_instruments/notifications-test.ts --exit --timeout 1m",
|
|
49
50
|
"test:tunnel-creation": "sudo tsx scripts/test-tunnel-creation.ts",
|
|
50
51
|
"test:tunnel-creation:lsof": "sudo tsx scripts/test-tunnel-creation.ts --keep-open"
|
|
51
52
|
},
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"@types/chai": "^5.2.1",
|
|
70
71
|
"@types/chai-as-promised": "^8.0.2",
|
|
71
72
|
"@types/mocha": "^10.0.10",
|
|
73
|
+
"@types/sinon": "^21.0.0",
|
|
72
74
|
"chai": "^6.0.1",
|
|
73
75
|
"chai-as-promised": "^8.0.1",
|
|
74
76
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
@@ -77,6 +79,7 @@
|
|
|
77
79
|
"prettier": "^3.5.3",
|
|
78
80
|
"rimraf": "^6.0.1",
|
|
79
81
|
"semantic-release": "^25.0.2",
|
|
82
|
+
"sinon": "^21.0.0",
|
|
80
83
|
"ts-node": "^10.9.2",
|
|
81
84
|
"tsx": "^4.7.0",
|
|
82
85
|
"typescript": "^5.2.2"
|
|
@@ -84,7 +87,7 @@
|
|
|
84
87
|
"dependencies": {
|
|
85
88
|
"@appium/strongbox": "^1.0.0-rc.1",
|
|
86
89
|
"@appium/support": "^7.0.0-rc.1",
|
|
87
|
-
"@types/node": "^
|
|
90
|
+
"@types/node": "^25.0.2",
|
|
88
91
|
"@xmldom/xmldom": "^0.9.8",
|
|
89
92
|
"appium-ios-tuntap": "^0.x",
|
|
90
93
|
"axios": "^1.12.0",
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type KeyPairKeyObjectResult,
|
|
3
|
-
generateKeyPairSync,
|
|
4
|
-
sign,
|
|
5
|
-
} from 'node:crypto';
|
|
1
|
+
import { generateKeyPairSync, sign } from 'node:crypto';
|
|
6
2
|
|
|
7
3
|
import { getLogger } from '../../logger.js';
|
|
8
4
|
import { CryptographyError } from '../errors.js';
|
|
@@ -24,7 +20,7 @@ const ED25519_PKCS8_PREFIX = Buffer.from(
|
|
|
24
20
|
*/
|
|
25
21
|
export function generateEd25519KeyPair(): PairingKeys {
|
|
26
22
|
try {
|
|
27
|
-
const keyPair
|
|
23
|
+
const keyPair = generateKeyPairSync('ed25519');
|
|
28
24
|
|
|
29
25
|
const publicKeyDer = keyPair.publicKey.export({
|
|
30
26
|
type: 'spki',
|
package/src/lib/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { ServiceConnection } from '../service-connection.js';
|
|
|
8
8
|
import type { BaseService, Service } from '../services/ios/base-service.js';
|
|
9
9
|
import type { iOSApplication } from '../services/ios/dvt/instruments/application-listing.js';
|
|
10
10
|
import type { LocationCoordinates } from '../services/ios/dvt/instruments/location-simulation.js';
|
|
11
|
+
import type { NotificationMessage } from '../services/ios/dvt/instruments/notifications.js';
|
|
11
12
|
import { ProvisioningProfile } from '../services/ios/misagent/provisioning-profile.js';
|
|
12
13
|
import type { PowerAssertionOptions } from '../services/ios/power-assertion/index.js';
|
|
13
14
|
import { PowerAssertionType } from '../services/ios/power-assertion/index.js';
|
|
@@ -796,6 +797,50 @@ export interface DeviceInfoService {
|
|
|
796
797
|
nameForGid(gid: number): Promise<string>;
|
|
797
798
|
}
|
|
798
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Notification service monitor memory and app notifications
|
|
802
|
+
*/
|
|
803
|
+
export interface NotificationService {
|
|
804
|
+
/**
|
|
805
|
+
* Yields notification from memory and application state changes
|
|
806
|
+
* @example:
|
|
807
|
+
* {
|
|
808
|
+
* selector: 'applicationStateNotification:',
|
|
809
|
+
* data:
|
|
810
|
+
* {
|
|
811
|
+
* mach_absolute_time: 58061793038,
|
|
812
|
+
* execName: '/Applications/Spotlight.app',
|
|
813
|
+
* appName: 'Spotlight',
|
|
814
|
+
* pid: 327,
|
|
815
|
+
* state_description: 'Suspended'
|
|
816
|
+
* }
|
|
817
|
+
* },
|
|
818
|
+
* {
|
|
819
|
+
* selector: 'applicationStateNotification:',
|
|
820
|
+
* data:
|
|
821
|
+
* {
|
|
822
|
+
* mach_absolute_time: 58061827502,
|
|
823
|
+
* execName: '/private/var/containers/Bundle/Application/28AF0B11-363A-4242-9164-CF690064402B/MobileCal.app',
|
|
824
|
+
* appName: 'MobileCal',
|
|
825
|
+
* pid: 449,
|
|
826
|
+
* state_description: 'Suspended'
|
|
827
|
+
* }
|
|
828
|
+
* },
|
|
829
|
+
* {
|
|
830
|
+
* selector: 'memoryLevelNotification:',
|
|
831
|
+
* data:
|
|
832
|
+
* {
|
|
833
|
+
* code: 3,
|
|
834
|
+
* mach_absolute_time: 101524320437,
|
|
835
|
+
* timestamp: [Object],
|
|
836
|
+
* pid: -1
|
|
837
|
+
* }
|
|
838
|
+
*
|
|
839
|
+
* }
|
|
840
|
+
*/
|
|
841
|
+
messages(): AsyncGenerator<NotificationMessage, void, undefined>;
|
|
842
|
+
}
|
|
843
|
+
|
|
799
844
|
/**
|
|
800
845
|
* DVT service with connection
|
|
801
846
|
* This allows callers to properly manage the connection lifecycle
|
|
@@ -815,6 +860,8 @@ export interface DVTServiceWithConnection {
|
|
|
815
860
|
graphics: GraphicsService;
|
|
816
861
|
/** The DeviceInfo service instance */
|
|
817
862
|
deviceInfo: DeviceInfoService;
|
|
863
|
+
/** The Notifications service instance */
|
|
864
|
+
notification: NotificationService;
|
|
818
865
|
/** The RemoteXPC connection that can be used to close the connection */
|
|
819
866
|
remoteXPC: RemoteXpcConnection;
|
|
820
867
|
}
|
|
@@ -23,6 +23,14 @@ export class Channel {
|
|
|
23
23
|
return data;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Receive a plist response from the channel with auxiliaries
|
|
28
|
+
* @returns Tuple of [selector, auxiliaries]
|
|
29
|
+
*/
|
|
30
|
+
async receivePlistWithAux(): Promise<[string, any[]]> {
|
|
31
|
+
return await this.service.recvPlist(this.channelCode);
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
/**
|
|
27
35
|
* Call a method on this channel with automatic ObjectiveC selector conversion
|
|
28
36
|
*
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
-
import type { Channel } from '../channel.js';
|
|
3
2
|
import { MessageAux } from '../dtx-message.js';
|
|
4
|
-
import
|
|
3
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
5
4
|
|
|
6
5
|
const log = getLogger('ApplicationListing');
|
|
7
6
|
|
|
@@ -46,24 +45,10 @@ export interface iOSApplication {
|
|
|
46
45
|
/**
|
|
47
46
|
* Application Listing service for retrieving installed applications
|
|
48
47
|
*/
|
|
49
|
-
export class ApplicationListing {
|
|
48
|
+
export class ApplicationListing extends BaseInstrument {
|
|
50
49
|
static readonly IDENTIFIER =
|
|
51
50
|
'com.apple.instruments.server.services.device.applictionListing';
|
|
52
51
|
|
|
53
|
-
private channel: Channel | null = null;
|
|
54
|
-
|
|
55
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Initialize the application listing channel
|
|
59
|
-
*/
|
|
60
|
-
async initialize(): Promise<void> {
|
|
61
|
-
if (this.channel) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
this.channel = await this.dvt.makeChannel(ApplicationListing.IDENTIFIER);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
52
|
/**
|
|
68
53
|
* Get the list of installed applications from the device
|
|
69
54
|
* @returns {Promise<iOSApplication[]>}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Channel } from '../channel.js';
|
|
2
|
+
import type { DVTSecureSocketProxyService } from '../index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base class for DVT instrument services.
|
|
6
|
+
*
|
|
7
|
+
* Subclasses must define a static `IDENTIFIER` property.
|
|
8
|
+
*/
|
|
9
|
+
export abstract class BaseInstrument {
|
|
10
|
+
static readonly IDENTIFIER: string;
|
|
11
|
+
|
|
12
|
+
protected channel: Channel | null = null;
|
|
13
|
+
constructor(protected readonly dvt: DVTSecureSocketProxyService) {}
|
|
14
|
+
|
|
15
|
+
protected get identifier(): string {
|
|
16
|
+
return (this.constructor as typeof BaseInstrument).IDENTIFIER;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the instrument channel.
|
|
21
|
+
*/
|
|
22
|
+
async initialize(): Promise<void> {
|
|
23
|
+
if (!this.channel) {
|
|
24
|
+
this.channel = await this.dvt.makeChannel(this.identifier);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
2
|
import type { ConditionGroup } from '../../../../lib/types.js';
|
|
3
|
-
import type { Channel } from '../channel.js';
|
|
4
3
|
import { MessageAux } from '../dtx-message.js';
|
|
5
|
-
import
|
|
4
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
6
5
|
|
|
7
6
|
const log = getLogger('ConditionInducer');
|
|
8
7
|
|
|
@@ -10,24 +9,10 @@ const log = getLogger('ConditionInducer');
|
|
|
10
9
|
* Condition Inducer service for simulating various device conditions
|
|
11
10
|
* such as network conditions, thermal states, etc.
|
|
12
11
|
*/
|
|
13
|
-
export class ConditionInducer {
|
|
12
|
+
export class ConditionInducer extends BaseInstrument {
|
|
14
13
|
static readonly IDENTIFIER =
|
|
15
14
|
'com.apple.instruments.server.services.ConditionInducer';
|
|
16
15
|
|
|
17
|
-
private channel: Channel | null = null;
|
|
18
|
-
|
|
19
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Initialize the condition inducer channel
|
|
23
|
-
*/
|
|
24
|
-
async initialize(): Promise<void> {
|
|
25
|
-
if (this.channel) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
this.channel = await this.dvt.makeChannel(ConditionInducer.IDENTIFIER);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
16
|
/**
|
|
32
17
|
* List all available condition inducers and their profiles
|
|
33
18
|
* @returns Array of condition groups with their available profiles
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
2
|
import { parseBinaryPlist } from '../../../../lib/plist/index.js';
|
|
3
3
|
import type { ProcessInfo } from '../../../../lib/types.js';
|
|
4
|
-
import type { Channel } from '../channel.js';
|
|
5
4
|
import { MessageAux } from '../dtx-message.js';
|
|
6
|
-
import
|
|
5
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
7
6
|
|
|
8
7
|
const log = getLogger('DeviceInfo');
|
|
9
8
|
|
|
@@ -25,22 +24,10 @@ const log = getLogger('DeviceInfo');
|
|
|
25
24
|
* - nameForUid(uid): Get username for UID
|
|
26
25
|
* - nameForGid(gid): Get group name for GID
|
|
27
26
|
*/
|
|
28
|
-
export class DeviceInfo {
|
|
27
|
+
export class DeviceInfo extends BaseInstrument {
|
|
29
28
|
static readonly IDENTIFIER =
|
|
30
29
|
'com.apple.instruments.server.services.deviceinfo';
|
|
31
30
|
|
|
32
|
-
private channel: Channel | null = null;
|
|
33
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize the device info channel
|
|
37
|
-
*/
|
|
38
|
-
async initialize(): Promise<void> {
|
|
39
|
-
if (!this.channel) {
|
|
40
|
-
this.channel = await this.dvt.makeChannel(DeviceInfo.IDENTIFIER);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
31
|
/**
|
|
45
32
|
* List directory contents at the specified path.
|
|
46
33
|
* @param path - The directory path to list
|
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
-
import type { Channel } from '../channel.js';
|
|
3
2
|
import { MessageAux } from '../dtx-message.js';
|
|
4
|
-
import
|
|
3
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
5
4
|
|
|
6
5
|
const log = getLogger('Graphics');
|
|
7
6
|
|
|
8
|
-
export class Graphics {
|
|
7
|
+
export class Graphics extends BaseInstrument {
|
|
9
8
|
static readonly IDENTIFIER =
|
|
10
9
|
'com.apple.instruments.server.services.graphics.opengl';
|
|
11
10
|
|
|
12
|
-
private channel: Channel | null = null;
|
|
13
|
-
|
|
14
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
15
|
-
|
|
16
|
-
async initialize(): Promise<void> {
|
|
17
|
-
if (!this.channel) {
|
|
18
|
-
this.channel = await this.dvt.makeChannel(Graphics.IDENTIFIER);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
11
|
async start(): Promise<void> {
|
|
23
12
|
await this.initialize();
|
|
24
13
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
-
import type { Channel } from '../channel.js';
|
|
3
2
|
import { MessageAux } from '../dtx-message.js';
|
|
4
|
-
import
|
|
3
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
5
4
|
|
|
6
5
|
const log = getLogger('LocationSimulation');
|
|
7
6
|
|
|
@@ -16,25 +15,10 @@ export interface LocationCoordinates {
|
|
|
16
15
|
/**
|
|
17
16
|
* Location simulation service for simulating device GPS location
|
|
18
17
|
*/
|
|
19
|
-
export class LocationSimulation {
|
|
18
|
+
export class LocationSimulation extends BaseInstrument {
|
|
20
19
|
static readonly IDENTIFIER =
|
|
21
20
|
'com.apple.instruments.server.services.LocationSimulation';
|
|
22
21
|
|
|
23
|
-
private channel: Channel | null = null;
|
|
24
|
-
|
|
25
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Initialize the location simulation channel
|
|
29
|
-
*/
|
|
30
|
-
async initialize(): Promise<void> {
|
|
31
|
-
if (this.channel) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.channel = await this.dvt.makeChannel(LocationSimulation.IDENTIFIER);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
22
|
/**
|
|
39
23
|
* Set the simulated GPS location
|
|
40
24
|
* @param coordinates The location coordinates
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
+
import { MessageAux } from '../dtx-message.js';
|
|
3
|
+
import { decodeNSKeyedArchiver } from '../nskeyedarchiver-decoder.js';
|
|
4
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
5
|
+
|
|
6
|
+
const log = getLogger('Notifications');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Application state notification data structure
|
|
10
|
+
*/
|
|
11
|
+
export interface ApplicationStateNotificationData {
|
|
12
|
+
/** High-precision Mach absolute time timestamp */
|
|
13
|
+
mach_absolute_time: bigint;
|
|
14
|
+
/** Full path to the executable (e.g., '/private/var/containers/Bundle/Application/.../MobileCal.app') */
|
|
15
|
+
execName: string;
|
|
16
|
+
/** Short application name (e.g., 'MobileCal') */
|
|
17
|
+
appName: string;
|
|
18
|
+
/** Process ID of the application */
|
|
19
|
+
pid: number;
|
|
20
|
+
/** Application state: 'Foreground' | 'Background' | 'Suspended' | 'Terminated' */
|
|
21
|
+
state_description: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Memory level notification data structure
|
|
26
|
+
*/
|
|
27
|
+
export interface MemoryLevelNotificationData {
|
|
28
|
+
/** Memory pressure level code (0=Normal, 1=Warning, 2=Critical, 3=...) */
|
|
29
|
+
code: number;
|
|
30
|
+
/** High-precision Mach absolute time timestamp */
|
|
31
|
+
mach_absolute_time: bigint;
|
|
32
|
+
/** NSDate timestamp object with NS.time property */
|
|
33
|
+
timestamp: number;
|
|
34
|
+
/** Process ID (-1 for system-wide notifications) */
|
|
35
|
+
pid: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Application state notification message
|
|
40
|
+
*/
|
|
41
|
+
export interface ApplicationStateNotification {
|
|
42
|
+
selector: 'applicationStateNotification:';
|
|
43
|
+
data: ApplicationStateNotificationData;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Memory level notification message
|
|
48
|
+
*/
|
|
49
|
+
export interface MemoryLevelNotification {
|
|
50
|
+
selector: 'memoryLevelNotification:';
|
|
51
|
+
data: MemoryLevelNotificationData;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Monitor memory and app notification
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* for await (const msg of notifications.messages()) {
|
|
60
|
+
* if (!msg) continue;
|
|
61
|
+
*
|
|
62
|
+
* if (msg.selector === 'applicationStateNotification:') {
|
|
63
|
+
* const notif = msg.data;
|
|
64
|
+
* console.log(`${notif.appName} is ${notif.state_description}`);
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export type NotificationMessage =
|
|
70
|
+
| ApplicationStateNotification
|
|
71
|
+
| MemoryLevelNotification;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Notifications service for monitoring iOS system events
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* for await (const msg of dvtService.notifications.messages()) {
|
|
79
|
+
* if (!msg) continue;
|
|
80
|
+
*
|
|
81
|
+
* if (msg.selector === 'applicationStateNotification:') {
|
|
82
|
+
* const app = msg.data;
|
|
83
|
+
* console.log(`${app.appName}: ${app.state_description}`);
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export class Notifications extends BaseInstrument {
|
|
89
|
+
/** DTX service identifier for mobile notifications */
|
|
90
|
+
static readonly IDENTIFIER =
|
|
91
|
+
'com.apple.instruments.server.services.mobilenotifications';
|
|
92
|
+
|
|
93
|
+
async start(): Promise<void> {
|
|
94
|
+
await this.initialize();
|
|
95
|
+
const args = new MessageAux().appendObj(true);
|
|
96
|
+
await this.channel!.call('setApplicationStateNotificationsEnabled_')(args);
|
|
97
|
+
await this.channel!.call('setMemoryNotificationsEnabled_')(args);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async stop(): Promise<void> {
|
|
101
|
+
const args = new MessageAux().appendObj(false);
|
|
102
|
+
await this.channel!.call('setApplicationStateNotificationsEnabled_')(args);
|
|
103
|
+
await this.channel!.call('setMemoryNotificationsEnabled_')(args);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Yields notification messages from the iOS device
|
|
108
|
+
*/
|
|
109
|
+
async *messages(): AsyncGenerator<NotificationMessage, void, undefined> {
|
|
110
|
+
log.debug('Network monitoring has started');
|
|
111
|
+
await this.start();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
while (true) {
|
|
115
|
+
const [selector, auxiliaries] =
|
|
116
|
+
await this.channel!.receivePlistWithAux();
|
|
117
|
+
|
|
118
|
+
// Decode NSKeyedArchiver format in auxiliaries first index
|
|
119
|
+
const decodedData = auxiliaries[0];
|
|
120
|
+
if (
|
|
121
|
+
decodedData &&
|
|
122
|
+
typeof decodedData === 'object' &&
|
|
123
|
+
decodedData.$archiver === 'NSKeyedArchiver'
|
|
124
|
+
) {
|
|
125
|
+
try {
|
|
126
|
+
const data = decodeNSKeyedArchiver(decodedData);
|
|
127
|
+
yield {
|
|
128
|
+
selector: selector as
|
|
129
|
+
| 'applicationStateNotification:'
|
|
130
|
+
| 'memoryLevelNotification:',
|
|
131
|
+
data,
|
|
132
|
+
} as NotificationMessage;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
log.warn('Failed to decode NSKeyedArchiver data:', error);
|
|
135
|
+
await this.stop();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
log.debug('Network monitoring has ended');
|
|
141
|
+
await this.stop();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
-
import
|
|
3
|
-
import type { DVTSecureSocketProxyService } from '../index.js';
|
|
2
|
+
import { BaseInstrument } from './base-instrument.js';
|
|
4
3
|
|
|
5
4
|
const log = getLogger('Screenshot');
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Screenshot service for capturing device screenshots
|
|
9
8
|
*/
|
|
10
|
-
export class Screenshot {
|
|
9
|
+
export class Screenshot extends BaseInstrument {
|
|
11
10
|
static readonly IDENTIFIER =
|
|
12
11
|
'com.apple.instruments.server.services.screenshot';
|
|
13
12
|
|
|
14
|
-
private channel: Channel | null = null;
|
|
15
|
-
|
|
16
|
-
constructor(private readonly dvt: DVTSecureSocketProxyService) {}
|
|
17
|
-
|
|
18
|
-
async initialize(): Promise<void> {
|
|
19
|
-
if (!this.channel) {
|
|
20
|
-
this.channel = await this.dvt.makeChannel(Screenshot.IDENTIFIER);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
13
|
/**
|
|
25
14
|
* Capture a screenshot from the device
|
|
26
15
|
* @returns The screenshot data as a Buffer
|