Skip to content

Commit 9f04b1b

Browse files
Add optional timeout to android ShellCommand commands in client.
Enabling subprocess timeouts for ATP when running tests - Part 2 PiperOrigin-RevId: 329932433
1 parent 39788c7 commit 9f04b1b

File tree

5 files changed

+94
-15
lines changed

5 files changed

+94
-15
lines changed

services/shellexecutor/java/androidx/test/services/shellexecutor/ShellCommandClient.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public static synchronized InputStream execOnServer(
6868
String command,
6969
List<String> parameters,
7070
Map<String, String> shellEnv,
71-
boolean executeThroughShell)
71+
boolean executeThroughShell,
72+
long timeoutMs)
7273
throws ClientNotConnected, IOException, RemoteException {
7374

7475
if (TextUtils.isEmpty(command)) {
@@ -103,7 +104,14 @@ public static synchronized InputStream execOnServer(
103104
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
104105

105106
Command commandStub = Stub.asInterface(result.binder);
106-
commandStub.execute(command, parameters, shellEnv, executeThroughShell, pipe[1]);
107+
// Only use timeout version if timeout is greater than 0
108+
if (timeoutMs > 0L) {
109+
// NOTICE: this is not be supported on older versions of the Command server.
110+
commandStub.executeWithTimeout(
111+
command, parameters, shellEnv, executeThroughShell, pipe[1], timeoutMs);
112+
} else {
113+
commandStub.execute(command, parameters, shellEnv, executeThroughShell, pipe[1]);
114+
}
107115

108116
// Closes the write pipe client-side. Server-side to be closed by server.
109117
pipe[1].close();
@@ -129,10 +137,12 @@ public static synchronized String execOnServerSync(
129137
String command,
130138
List<String> parameters,
131139
Map<String, String> shellEnv,
132-
boolean executeThroughShell)
140+
boolean executeThroughShell,
141+
long timeoutMs)
133142
throws ClientNotConnected, IOException, RemoteException {
134143
return inputStreamToString(
135-
execOnServer(context, secret, command, parameters, shellEnv, executeThroughShell));
144+
execOnServer(
145+
context, secret, command, parameters, shellEnv, executeThroughShell, timeoutMs));
136146
}
137147

138148
private static String inputStreamToString(InputStream inputStream) throws IOException {

services/shellexecutor/java/androidx/test/services/shellexecutor/ShellExecutor.java

+44-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,28 @@ public interface ShellExecutor {
3838
* @param shellEnv A {@link Map} of shell environment variables to be set
3939
* @param executeThroughShell If set to true, the command string will be executed through the
4040
* shell with parameters given as additional shell arguments.
41-
* @return @link{String} representing the contents of the shell output of the command.
41+
* @param timeoutMs Optional, destroys the executing subprocess if it runs longer than this
42+
* timeout.
43+
* @return {@link String} representing the contents of the shell output of the command.
44+
* @throws IOException if cannot execute command on executor service.
45+
*/
46+
String executeShellCommandSync(
47+
String command,
48+
List<String> parameters,
49+
Map<String, String> shellEnv,
50+
boolean executeThroughShell,
51+
long timeoutMs)
52+
throws ClientNotConnected, IOException, RemoteException;
53+
54+
/**
55+
* Execute a command with elevated permissions and block.
56+
*
57+
* @param command The shell command to be executed.
58+
* @param parameters A {@link Map} parameters to be given to the shell command
59+
* @param shellEnv A {@link Map} of shell environment variables to be set
60+
* @param executeThroughShell If set to true, the command string will be executed through the
61+
* shell with parameters given as additional shell arguments.
62+
* @return {@link String} representing the contents of the shell output of the command.
4263
* @throws IOException if cannot execute command on executor service.
4364
*/
4465
String executeShellCommandSync(
@@ -56,7 +77,28 @@ String executeShellCommandSync(
5677
* @param shellEnv A {@link Map} of shell environment variables to be set
5778
* @param executeThroughShell If set to true, the command string will be executed through the
5879
* shell with parameters given as additional shell arguments.
59-
* @return @link{java.io.InputStream} representing the shell output of the command.
80+
* @param timeoutMs Optional, destroys the executing subprocess if it runs longer than this
81+
* timeout.
82+
* @return {@link java.io.InputStream} representing the shell output of the command.
83+
* @throws IOException if cannot execute command on executor service.
84+
*/
85+
InputStream executeShellCommand(
86+
String command,
87+
List<String> parameters,
88+
Map<String, String> shellEnv,
89+
boolean executeThroughShell,
90+
long timeoutMs)
91+
throws ClientNotConnected, IOException, RemoteException;
92+
93+
/**
94+
* Execute a command with elevated permissions and return immediately.
95+
*
96+
* @param command The shell command to be executed.
97+
* @param parameters A {@link Map} parameters to be given to the shell command
98+
* @param shellEnv A {@link Map} of shell environment variables to be set
99+
* @param executeThroughShell If set to true, the command string will be executed through the
100+
* shell with parameters given as additional shell arguments.
101+
* @return {@link java.io.InputStream} representing the shell output of the command.
60102
* @throws IOException if cannot execute command on executor service.
61103
*/
62104
InputStream executeShellCommand(

services/shellexecutor/java/androidx/test/services/shellexecutor/ShellExecutorImpl.java

+28-4
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ public String executeShellCommandSync(
5050
String command,
5151
List<String> parameters,
5252
Map<String, String> shellEnv,
53-
boolean executeThroughShell)
53+
boolean executeThroughShell,
54+
long timeoutMs)
5455
throws IOException {
5556
try {
5657
return ShellCommandClient.execOnServerSync(
57-
context, binderKey, command, parameters, shellEnv, executeThroughShell);
58+
context, binderKey, command, parameters, shellEnv, executeThroughShell, timeoutMs);
5859
} catch (ClientNotConnected e) {
5960
Log.e(TAG, "ShellCommandClient not connected. Is ShellCommandExecutor service started?", e);
6061
throw new RuntimeException(e);
@@ -67,15 +68,27 @@ public String executeShellCommandSync(
6768

6869
/** {@inheritDoc} */
6970
@Override
70-
public InputStream executeShellCommand(
71+
public String executeShellCommandSync(
7172
String command,
7273
List<String> parameters,
7374
Map<String, String> shellEnv,
7475
boolean executeThroughShell)
76+
throws IOException {
77+
return executeShellCommandSync(command, parameters, shellEnv, executeThroughShell, 0L);
78+
}
79+
80+
/** {@inheritDoc} */
81+
@Override
82+
public InputStream executeShellCommand(
83+
String command,
84+
List<String> parameters,
85+
Map<String, String> shellEnv,
86+
boolean executeThroughShell,
87+
long timeoutMs)
7588
throws IOException, RemoteException {
7689
try {
7790
return ShellCommandClient.execOnServer(
78-
context, binderKey, command, parameters, shellEnv, executeThroughShell);
91+
context, binderKey, command, parameters, shellEnv, executeThroughShell, timeoutMs);
7992
} catch (ClientNotConnected e) {
8093
Log.e(TAG, "ShellCommandClient not connected. Is ShellCommandExecutor service started?", e);
8194
throw new RuntimeException(e);
@@ -85,4 +98,15 @@ public InputStream executeShellCommand(
8598
throw new RuntimeException(e);
8699
}
87100
}
101+
102+
/** {@inheritDoc} */
103+
@Override
104+
public InputStream executeShellCommand(
105+
String command,
106+
List<String> parameters,
107+
Map<String, String> shellEnv,
108+
boolean executeThroughShell)
109+
throws IOException, RemoteException {
110+
return executeShellCommand(command, parameters, shellEnv, executeThroughShell, 0L);
111+
}
88112
}

services/shellexecutor/javatests/androidx/test/services/shellexecutor/ShellCommandClientTest.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class ShellCommandClientTest {
3535
public void testBlankCommand()
3636
throws ClientNotConnected, IOException, RemoteException, InterruptedException {
3737
try {
38-
ShellCommandClient.execOnServer(getTargetContext(), "secret", "", null, null, false);
38+
ShellCommandClient.execOnServer(getTargetContext(), "secret", "", null, null, false, 0L);
3939
fail("Passing blank command should throw IllegalArgumentException");
4040
} catch (IllegalArgumentException expected) {
4141
// Pass
@@ -46,7 +46,7 @@ public void testBlankCommand()
4646
public void testNullCommand()
4747
throws ClientNotConnected, IOException, RemoteException, InterruptedException {
4848
try {
49-
ShellCommandClient.execOnServer(getTargetContext(), "secret", null, null, null, false);
49+
ShellCommandClient.execOnServer(getTargetContext(), "secret", null, null, null, false, 0L);
5050
fail("Passing null command should throw IllegalArgumentException");
5151
} catch (IllegalArgumentException expected) {
5252
// Pass
@@ -57,7 +57,8 @@ public void testNullCommand()
5757
public void testShellClientNoRunOnMainThread()
5858
throws ClientNotConnected, IOException, RemoteException, InterruptedException {
5959
try {
60-
ShellCommandClient.execOnServer(getTargetContext(), "secret", "command", null, null, false);
60+
ShellCommandClient.execOnServer(
61+
getTargetContext(), "secret", "command", null, null, false, 0L);
6162
fail("Calling execServer on main thread should throw exception");
6263
} catch (IllegalStateException expected) {
6364
// Pass

services/shellexecutor/javatests/androidx/test/services/shellexecutor/ShellCommandTest.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ private static String execShellCommand(
5353
command,
5454
params,
5555
env,
56-
executeThroughShell);
56+
executeThroughShell,
57+
0L);
5758
}
5859

5960
@Test
@@ -127,7 +128,8 @@ public void testLargeFileDump() throws Exception {
127128
"dd if=/dev/urandom bs=2048 count=16384",
128129
null,
129130
null,
130-
true);
131+
true,
132+
0L);
131133

132134
boolean weReadSomething = false;
133135

0 commit comments

Comments
 (0)