Skip to content

Commit 45ad814

Browse files
authored
fix(android): drop invalid -d display flag from yadb app_process commands (#2718)
1 parent ae08980 commit 45ad814

2 files changed

Lines changed: 103 additions & 3 deletions

File tree

packages/android/src/device.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ export class AndroidDevice implements AbstractInterface {
148148
}
149149
},
150150
pinch: async (center, opts) => {
151+
// yadb only injects gestures into the default display, so a non-default
152+
// display would silently land the pinch on the main screen. Fail fast
153+
// instead of misleading the caller.
154+
if (
155+
typeof this.options?.displayId === 'number' &&
156+
this.options.displayId !== 0
157+
) {
158+
throw new Error(
159+
`Pinch is not supported on a non-default display (displayId=${this.options.displayId}). The underlying yadb tool only injects gestures into the default display.`,
160+
);
161+
}
151162
const { x: adjCenterX, y: adjCenterY } = await this.adjustCoordinates(
152163
Math.round(center.x),
153164
Math.round(center.y),
@@ -159,7 +170,10 @@ export class AndroidDevice implements AbstractInterface {
159170
await this.ensureYadb();
160171
const adb = await this.getAdb();
161172
await adb.shell(
162-
`app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -pinch ${adjCenterX} ${adjCenterY} ${adjStartDist} ${adjEndDist} ${opts.duration}`,
173+
// Note: do not append getDisplayArg() here. `app_process` is the ART
174+
// runtime launcher and does not accept the `-d <displayId>` flag the
175+
// way `input`/`dumpsys` do; passing it makes the VM fail to start.
176+
`app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -pinch ${adjCenterX} ${adjCenterY} ${adjStartDist} ${adjEndDist} ${opts.duration}`,
163177
);
164178
},
165179
},
@@ -557,12 +571,14 @@ ${Object.keys(size)
557571
}
558572

559573
async execYadb(keyboardContent: string): Promise<void> {
574+
this.warnYadbOnNonDefaultDisplay('keyboard input');
560575
await this.ensureYadb();
561576

562577
const adb = await this.getAdb();
563578

564579
await adb.shell(
565-
`app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard '${keyboardContent}'`,
580+
// `app_process` (ART launcher) does not accept the `-d <displayId>` flag.
581+
`app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard '${keyboardContent}'`,
566582
);
567583
}
568584

@@ -1196,8 +1212,10 @@ ${Object.keys(size)
11961212
await adb.clearTextField(100);
11971213
} else {
11981214
// Use the yadb tool to clear the input box
1215+
this.warnYadbOnNonDefaultDisplay('keyboard clear');
11991216
await adb.shell(
1200-
`app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboardClear`,
1217+
// `app_process` (ART launcher) does not accept the `-d <displayId>` flag.
1218+
'app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboardClear',
12011219
);
12021220
}
12031221

@@ -1892,6 +1910,24 @@ ${Object.keys(size)
18921910
: '';
18931911
}
18941912

1913+
/**
1914+
* yadb (launched via `app_process`) cannot target a specific display and
1915+
* always acts on the default display. When a non-default display is
1916+
* configured, warn so the caller knows the operation lands on the main
1917+
* screen instead of failing silently. Unlike pinch, keyboard operations also
1918+
* have an `input`-based path, so we warn rather than throw.
1919+
*/
1920+
private warnYadbOnNonDefaultDisplay(operation: string): void {
1921+
if (
1922+
typeof this.options?.displayId === 'number' &&
1923+
this.options.displayId !== 0
1924+
) {
1925+
warnDevice(
1926+
`yadb ${operation} cannot target display ${this.options.displayId}; it will act on the default display.`,
1927+
);
1928+
}
1929+
}
1930+
18951931
async getPhysicalDisplayId(): Promise<string | null> {
18961932
// Return cached value if available
18971933
if (this.cachedPhysicalDisplayId !== undefined) {

packages/android/tests/unit-test/page.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,70 @@ Stdout:
22312231
expect.stringContaining('input -d 2 swipe'),
22322232
);
22332233
});
2234+
2235+
it('should NOT pass the display argument to app_process (yadb) pinch even when displayId is set', async () => {
2236+
// `app_process` is the ART runtime launcher and does not accept the
2237+
// `-d <displayId>` flag the way `input`/`dumpsys` do. Passing it makes
2238+
// the VM fail to start and breaks aiPinch. See getDisplayArg().
2239+
deviceWithDisplay = new AndroidDevice('test-device', {
2240+
displayId: 0,
2241+
});
2242+
2243+
setupMockAdb(mockAdbInstance);
2244+
2245+
vi.spyOn(deviceWithDisplay, 'getAdb').mockResolvedValue(
2246+
mockAdbInstance as any,
2247+
);
2248+
vi.spyOn(deviceWithDisplay as any, 'ensureYadb').mockResolvedValue(
2249+
undefined,
2250+
);
2251+
(deviceWithDisplay as any).devicePixelRatio = 1;
2252+
2253+
await deviceWithDisplay.inputPrimitives.touch.pinch!(
2254+
{ x: 1280, y: 720 },
2255+
{ startDistance: 600, endDistance: 200, duration: 1200 },
2256+
);
2257+
2258+
const pinchCall = mockAdbInstance.shell.mock.calls.find(
2259+
(call: unknown[]) =>
2260+
typeof call[0] === 'string' && call[0].includes('-pinch'),
2261+
);
2262+
expect(pinchCall).toBeDefined();
2263+
expect(pinchCall![0]).toContain('app_process -Djava.class.path');
2264+
expect(pinchCall![0]).not.toContain('app_process -d');
2265+
});
2266+
2267+
it('should throw when pinch is called on a non-default display (displayId > 0)', async () => {
2268+
// yadb only injects into the default display, so a non-default display
2269+
// would silently land the pinch on the main screen. Fail fast instead.
2270+
deviceWithDisplay = new AndroidDevice('test-device', {
2271+
displayId: 2,
2272+
});
2273+
2274+
setupMockAdb(mockAdbInstance);
2275+
2276+
vi.spyOn(deviceWithDisplay, 'getAdb').mockResolvedValue(
2277+
mockAdbInstance as any,
2278+
);
2279+
vi.spyOn(deviceWithDisplay as any, 'ensureYadb').mockResolvedValue(
2280+
undefined,
2281+
);
2282+
(deviceWithDisplay as any).devicePixelRatio = 1;
2283+
2284+
await expect(
2285+
deviceWithDisplay.inputPrimitives.touch.pinch!(
2286+
{ x: 1280, y: 720 },
2287+
{ startDistance: 600, endDistance: 200, duration: 1200 },
2288+
),
2289+
).rejects.toThrow(/non-default display/);
2290+
2291+
// No yadb command should have been issued.
2292+
const pinchCall = mockAdbInstance.shell.mock.calls.find(
2293+
(call: unknown[]) =>
2294+
typeof call[0] === 'string' && call[0].includes('-pinch'),
2295+
);
2296+
expect(pinchCall).toBeUndefined();
2297+
});
22342298
});
22352299

22362300
it('should not include display argument in shell commands when displayId is not set', async () => {

0 commit comments

Comments
 (0)