Skip to content

Commit aabb9d3

Browse files
committed
Make ADB root check more reliable
Previously we repeated devices until the device appeared, but it seems that can happen before a shell is successful. Instead we now check whoami itself, until it works for real.
1 parent e5c8040 commit aabb9d3

File tree

2 files changed

+23
-15
lines changed

2 files changed

+23
-15
lines changed

src/interceptors/android/adb-commands.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as stream from 'stream';
22
import * as path from 'path';
33
import * as adb from '@devicefarmer/adbkit';
44
import { reportError } from '../../error-tracking';
5-
import { delay } from '../../util';
5+
import { delay, waitUntil } from '../../util';
66
import { getCertificateFingerprint, parseCert } from '../../certificates';
77

88
export const ANDROID_TEMP = '/data/local/tmp';
@@ -75,18 +75,7 @@ export const getConnectedDevices = batchCalls(async (adbClient: adb.AdbClient) =
7575
throw e;
7676
}
7777
}
78-
});
79-
80-
async function waitUntilAvailable(adbClient: adb.AdbClient, deviceId: string, tries: number) {
81-
delay(200);
82-
83-
while (tries > 0 && !(await getConnectedDevices(adbClient)).includes(deviceId)) {
84-
tries = tries - 1;
85-
await delay(500);
86-
}
87-
88-
if (tries <= 0) throw new Error(`Device ${deviceId} not available via ADB`);
89-
}
78+
})
9079

9180
export function stringAsStream(input: string) {
9281
const contentStream = new stream.Readable();
@@ -148,9 +137,11 @@ export async function getRootCommand(adbClient: adb.AdbClient, deviceId: string)
148137

149138
// Sometimes switching to root can disconnect ADB devices, so double-check
150139
// they're still here, and wait a few seconds for them to come back if not.
151-
await waitUntilAvailable(adbClient, deviceId, 10);
152140

153-
const whoami = await run(adbClient, deviceId, ['whoami']).catch(console.log);
141+
await delay(500); // Wait, since they may not disconnect immediately
142+
const whoami = await waitUntil(250, 10, (): Promise<string | false> => {
143+
return run(adbClient, deviceId, ['whoami']).catch(() => false)
144+
}).catch(console.log);
154145

155146
return (whoami || '').trim() === 'root'
156147
? [] // all commands now run as root, so no prefix required.

src/util.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ export function delay(durationMs: number): Promise<void> {
99
return new Promise((resolve) => setTimeout(resolve, durationMs));
1010
}
1111

12+
export async function waitUntil<T extends unknown>(
13+
delayMs: number,
14+
tries: number,
15+
test: () => Promise<T>
16+
): Promise<Exclude<T, false>> {
17+
let result = tries > 0 && await test()
18+
19+
while (tries > 0 && !result) {
20+
tries = tries - 1;
21+
await delay(delayMs);
22+
result = await test();
23+
}
24+
25+
if (!result) throw new Error(`Wait loop failed`);
26+
else return result as Exclude<T, false>;
27+
}
28+
1229
export interface Deferred<T> {
1330
resolve: (arg: T) => void,
1431
reject: (e?: Error) => void,

0 commit comments

Comments
 (0)