Skip to content

Commit f0d1ed2

Browse files
committed
Merge branch 'main' into release/v2
* main: Prepare for release 2.32.0. Add missing compiled js files from #383. Support customizing emulator port (#383)
2 parents 77986be + 81f860b commit f0d1ed2

11 files changed

+122
-33
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change Log
22

3+
## v2.32.0
4+
5+
* Add `port` parameter for customizing the emulator port to use. - [#383](https://github.com/ReactiveCircus/android-emulator-runner/pull/383)
6+
7+
38
## v2.31.0
49

510
* Support setting `VanillaIceCream` as `api-level`. - [#378](https://github.com/ReactiveCircus/android-emulator-runner/pull/378)

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ jobs:
191191
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
192192
| `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. |
193193
| `emulator-boot-timeout` | Optional | `600` | Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes. |
194+
| `emulator-port` | Optional | `5554` | Emulator port to use. Allows to run this action on multiple workers on a single machine at the same time. This input is available for the script as `EMULATOR_PORT` enviromental variable. This port is automatically used by android device related tasks in gradle |
194195
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
195196
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
196197
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |

__tests__/input-validator.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as validator from '../src/input-validator';
2+
import { MAX_PORT, MIN_PORT } from '../src/input-validator';
23

34
describe('api-level validator tests', () => {
45
it('Throws if api-level is not a number', () => {
@@ -172,6 +173,33 @@ describe('force-avd-creation validator tests', () => {
172173
});
173174
});
174175

176+
describe('emulator-port validator tests', () => {
177+
it('Validates if emulator-port is even and in range', () => {
178+
const func = () => {
179+
validator.checkPort(5554);
180+
};
181+
expect(func).not.toThrow();
182+
});
183+
it('Throws if emulator-port is lower than MIN_PORT', () => {
184+
const func = () => {
185+
validator.checkPort(MIN_PORT - 2);
186+
};
187+
expect(func).toThrow();
188+
});
189+
it('Throws if emulator-port is higher than MAX_PORT', () => {
190+
const func = () => {
191+
validator.checkPort(MAX_PORT + 2);
192+
};
193+
expect(func).toThrow();
194+
});
195+
it('Throws if emulator-port is odd', () => {
196+
const func = () => {
197+
validator.checkPort(5555);
198+
};
199+
expect(func).toThrow();
200+
});
201+
});
202+
175203
describe('disable-animations validator tests', () => {
176204
it('Throws if disable-animations is not a boolean', () => {
177205
const func = () => {

action-types.yml

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ inputs:
3737
type: boolean
3838
emulator-boot-timeout:
3939
type: integer
40+
emulator-port:
41+
type: integer
4042
emulator-options:
4143
type: string
4244
disable-animations:

action.yml

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ inputs:
3636
emulator-boot-timeout:
3737
description: 'Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes'
3838
default: '600'
39+
emulator-port:
40+
description: 'Port to run emulator on, allows to run multiple emulators on the same physical machine'
41+
default: '5554'
3942
emulator-options:
4043
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
4144
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'

lib/emulator-manager.js

+18-13
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const fs = __importStar(require("fs"));
3838
/**
3939
* Creates and launches a new AVD instance with the specified configurations.
4040
*/
41-
function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
41+
function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
4242
return __awaiter(this, void 0, void 0, function* () {
4343
try {
4444
console.log(`::group::Launch Emulator`);
@@ -72,7 +72,7 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
7272
}
7373
// start emulator
7474
console.log('Starting emulator.');
75-
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
75+
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
7676
listeners: {
7777
stderr: (data) => {
7878
if (data.toString().includes('invalid command-line parameter')) {
@@ -82,19 +82,19 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
8282
},
8383
});
8484
// wait for emulator to complete booting
85-
yield waitForDevice(emulatorBootTimeout);
86-
yield exec.exec(`adb shell input keyevent 82`);
85+
yield waitForDevice(port, emulatorBootTimeout);
86+
yield adb(port, `shell input keyevent 82`);
8787
if (disableAnimations) {
8888
console.log('Disabling animations.');
89-
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
90-
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
91-
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
89+
yield adb(port, `shell settings put global window_animation_scale 0.0`);
90+
yield adb(port, `shell settings put global transition_animation_scale 0.0`);
91+
yield adb(port, `shell settings put global animator_duration_scale 0.0`);
9292
}
9393
if (disableSpellChecker) {
94-
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
94+
yield adb(port, `shell settings put secure spell_checker_enabled 0`);
9595
}
9696
if (enableHardwareKeyboard) {
97-
yield exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
97+
yield adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
9898
}
9999
}
100100
finally {
@@ -106,11 +106,11 @@ exports.launchEmulator = launchEmulator;
106106
/**
107107
* Kills the running emulator on the default port.
108108
*/
109-
function killEmulator() {
109+
function killEmulator(port) {
110110
return __awaiter(this, void 0, void 0, function* () {
111111
try {
112112
console.log(`::group::Terminate Emulator`);
113-
yield exec.exec(`adb -s emulator-5554 emu kill`);
113+
yield adb(port, `emu kill`);
114114
}
115115
catch (error) {
116116
console.log(error instanceof Error ? error.message : error);
@@ -121,10 +121,15 @@ function killEmulator() {
121121
});
122122
}
123123
exports.killEmulator = killEmulator;
124+
function adb(port, command) {
125+
return __awaiter(this, void 0, void 0, function* () {
126+
return yield exec.exec(`adb -s emulator-${port} ${command}`);
127+
});
128+
}
124129
/**
125130
* Wait for emulator to boot.
126131
*/
127-
function waitForDevice(emulatorBootTimeout) {
132+
function waitForDevice(port, emulatorBootTimeout) {
128133
return __awaiter(this, void 0, void 0, function* () {
129134
let booted = false;
130135
let attempts = 0;
@@ -133,7 +138,7 @@ function waitForDevice(emulatorBootTimeout) {
133138
while (!booted) {
134139
try {
135140
let result = '';
136-
yield exec.exec(`adb shell getprop sys.boot_completed`, [], {
141+
yield exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
137142
listeners: {
138143
stdout: (data) => {
139144
result += data.toString();

lib/input-validator.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
3-
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
3+
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
44
exports.MIN_API_LEVEL = 15;
55
exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
66
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
77
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
8+
exports.MIN_PORT = 5554;
9+
exports.MAX_PORT = 5584;
810
exports.PREVIEW_API_LEVELS = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
911
function checkApiLevel(apiLevel) {
1012
if (exports.PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel)))
@@ -41,6 +43,15 @@ function checkForceAvdCreation(forceAvdCreation) {
4143
}
4244
}
4345
exports.checkForceAvdCreation = checkForceAvdCreation;
46+
function checkPort(port) {
47+
if (port < exports.MIN_PORT || port > exports.MAX_PORT) {
48+
throw new Error(`Emulator port is outside of the supported port range [${exports.MIN_PORT}, ${exports.MAX_PORT}], was ${port}`);
49+
}
50+
if (port % 2 == 1) {
51+
throw new Error(`Emulator port has to be even, was ${port}`);
52+
}
53+
}
54+
exports.checkPort = checkPort;
4455
function checkDisableAnimations(disableAnimations) {
4556
if (!isValidBoolean(disableAnimations)) {
4657
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);

lib/main.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const channel_id_mapper_1 = require("./channel-id-mapper");
4242
const fs_1 = require("fs");
4343
function run() {
4444
return __awaiter(this, void 0, void 0, function* () {
45+
let port = input_validator_1.MIN_PORT;
4546
try {
4647
console.log(`::group::Configure emulator`);
4748
let linuxSupportKVM = false;
@@ -102,6 +103,10 @@ function run() {
102103
// Emulator boot timeout seconds
103104
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
104105
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
106+
// Emulator port to use
107+
port = parseInt(core.getInput('emulator-port'), 10);
108+
(0, input_validator_1.checkPort)(port);
109+
console.log(`emulator port: ${port}`);
105110
// emulator options
106111
const emulatorOptions = core.getInput('emulator-options').trim();
107112
console.log(`emulator options: ${emulatorOptions}`);
@@ -193,7 +198,7 @@ function run() {
193198
console.log(`::endgroup::`);
194199
}
195200
// launch an emulator
196-
yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard);
201+
yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard);
197202
// execute the custom script
198203
try {
199204
// move to custom working directory if set
@@ -203,18 +208,20 @@ function run() {
203208
for (const script of scripts) {
204209
// use array form to avoid various quote escaping problems
205210
// caused by exec(`sh -c "${script}"`)
206-
yield exec.exec('sh', ['-c', script]);
211+
yield exec.exec('sh', ['-c', script], {
212+
env: Object.assign(Object.assign({}, process.env), { EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` }),
213+
});
207214
}
208215
}
209216
catch (error) {
210217
core.setFailed(error instanceof Error ? error.message : error);
211218
}
212219
// finally kill the emulator
213-
yield (0, emulator_manager_1.killEmulator)();
220+
yield (0, emulator_manager_1.killEmulator)(port);
214221
}
215222
catch (error) {
216223
// kill the emulator so the action can exit
217-
yield (0, emulator_manager_1.killEmulator)();
224+
yield (0, emulator_manager_1.killEmulator)(port);
218225
core.setFailed(error instanceof Error ? error.message : error);
219226
}
220227
});

src/emulator-manager.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function launchEmulator(
1717
avdName: string,
1818
forceAvdCreation: boolean,
1919
emulatorBootTimeout: number,
20+
port: number,
2021
emulatorOptions: string,
2122
disableAnimations: boolean,
2223
disableSpellChecker: boolean,
@@ -65,7 +66,7 @@ export async function launchEmulator(
6566
// start emulator
6667
console.log('Starting emulator.');
6768

68-
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
69+
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
6970
listeners: {
7071
stderr: (data: Buffer) => {
7172
if (data.toString().includes('invalid command-line parameter')) {
@@ -76,20 +77,20 @@ export async function launchEmulator(
7677
});
7778

7879
// wait for emulator to complete booting
79-
await waitForDevice(emulatorBootTimeout);
80-
await exec.exec(`adb shell input keyevent 82`);
80+
await waitForDevice(port, emulatorBootTimeout);
81+
await adb(port, `shell input keyevent 82`);
8182

8283
if (disableAnimations) {
8384
console.log('Disabling animations.');
84-
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
85-
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
86-
await exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
85+
await adb(port, `shell settings put global window_animation_scale 0.0`);
86+
await adb(port, `shell settings put global transition_animation_scale 0.0`);
87+
await adb(port, `shell settings put global animator_duration_scale 0.0`);
8788
}
8889
if (disableSpellChecker) {
89-
await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
90+
await adb(port, `shell settings put secure spell_checker_enabled 0`);
9091
}
9192
if (enableHardwareKeyboard) {
92-
await exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
93+
await adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
9394
}
9495
} finally {
9596
console.log(`::endgroup::`);
@@ -99,29 +100,33 @@ export async function launchEmulator(
99100
/**
100101
* Kills the running emulator on the default port.
101102
*/
102-
export async function killEmulator(): Promise<void> {
103+
export async function killEmulator(port: number): Promise<void> {
103104
try {
104105
console.log(`::group::Terminate Emulator`);
105-
await exec.exec(`adb -s emulator-5554 emu kill`);
106+
await adb(port, `emu kill`);
106107
} catch (error) {
107108
console.log(error instanceof Error ? error.message : error);
108109
} finally {
109110
console.log(`::endgroup::`);
110111
}
111112
}
112113

114+
async function adb(port: number, command: string): Promise<number> {
115+
return await exec.exec(`adb -s emulator-${port} ${command}`);
116+
}
117+
113118
/**
114119
* Wait for emulator to boot.
115120
*/
116-
async function waitForDevice(emulatorBootTimeout: number): Promise<void> {
121+
async function waitForDevice(port: number, emulatorBootTimeout: number): Promise<void> {
117122
let booted = false;
118123
let attempts = 0;
119124
const retryInterval = 2; // retry every 2 seconds
120125
const maxAttempts = emulatorBootTimeout / 2;
121126
while (!booted) {
122127
try {
123128
let result = '';
124-
await exec.exec(`adb shell getprop sys.boot_completed`, [], {
129+
await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
125130
listeners: {
126131
stdout: (data: Buffer) => {
127132
result += data.toString();

src/input-validator.ts

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export const MIN_API_LEVEL = 15;
22
export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
33
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
44
export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
5+
export const MIN_PORT = 5554;
6+
export const MAX_PORT = 5584;
57
export const PREVIEW_API_LEVELS: Array<string> = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
68

79
export function checkApiLevel(apiLevel: string): void {
@@ -38,6 +40,15 @@ export function checkForceAvdCreation(forceAvdCreation: string): void {
3840
}
3941
}
4042

43+
export function checkPort(port: number): void {
44+
if (port < MIN_PORT || port > MAX_PORT) {
45+
throw new Error(`Emulator port is outside of the supported port range [${MIN_PORT}, ${MAX_PORT}], was ${port}`);
46+
}
47+
if (port % 2 == 1) {
48+
throw new Error(`Emulator port has to be even, was ${port}`);
49+
}
50+
}
51+
4152
export function checkDisableAnimations(disableAnimations: string): void {
4253
if (!isValidBoolean(disableAnimations)) {
4354
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);

0 commit comments

Comments
 (0)