summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorEugene Susla <eugenesusla@google.com>2020-09-24 16:46:12 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-09-24 16:46:12 +0000
commitc50a911daa817d5aba2383286423a80d0473c1fc (patch)
tree0cff9841aaa081be5f16fcccabba1a5372cc5c18 /core/java
parent098991b929581d717d332171e8b9b7ec81fd69de (diff)
parent8fa2bdb099376894beccf0f2d4c561e1572dfeec (diff)
Merge "Run shell commands reliably"
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/IUiAutomationConnection.aidl2
-rw-r--r--core/java/android/app/UiAutomation.java48
-rw-r--r--core/java/android/app/UiAutomationConnection.java27
3 files changed, 72 insertions, 5 deletions
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 4c9e400681ee..9eeb9f6a95bf 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -52,4 +52,6 @@ interface IUiAutomationConnection {
void dropShellPermissionIdentity();
// Called from the system process.
oneway void shutdown();
+ void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink,
+ in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ab997e9f7832..4e868fe7370b 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -25,6 +25,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
@@ -1245,7 +1246,34 @@ public final class UiAutomation {
* @hide
*/
@TestApi
- public ParcelFileDescriptor[] executeShellCommandRw(String command) {
+ public @NonNull ParcelFileDescriptor[] executeShellCommandRw(@NonNull String command) {
+ return executeShellCommandInternal(command, false /* includeStderr */);
+ }
+
+ /**
+ * Executes a shell command. This method returns three file descriptors,
+ * one that points to the standard output stream (element at index 0), one that points
+ * to the standard input stream (element at index 1), and one points to
+ * standard error stream (element at index 2). The command execution is similar
+ * to running "adb shell <command>" from a host connected to the device.
+ * <p>
+ * <strong>Note:</strong> It is your responsibility to close the returned file
+ * descriptors once you are done reading/writing.
+ * </p>
+ *
+ * @param command The command to execute.
+ * @return File descriptors (out, in, err) to the standard output/input/error streams.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("ArrayReturn") // For consistency with other APIs
+ public @NonNull ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String command) {
+ return executeShellCommandInternal(command, true /* includeStderr */);
+ }
+
+ private ParcelFileDescriptor[] executeShellCommandInternal(
+ String command, boolean includeStderr) {
warnIfBetterCommand(command);
ParcelFileDescriptor source_read = null;
@@ -1254,6 +1282,9 @@ public final class UiAutomation {
ParcelFileDescriptor source_write = null;
ParcelFileDescriptor sink_write = null;
+ ParcelFileDescriptor stderr_source_read = null;
+ ParcelFileDescriptor stderr_sink_read = null;
+
try {
ParcelFileDescriptor[] pipe_read = ParcelFileDescriptor.createPipe();
source_read = pipe_read[0];
@@ -1263,8 +1294,15 @@ public final class UiAutomation {
source_write = pipe_write[0];
sink_write = pipe_write[1];
+ if (includeStderr) {
+ ParcelFileDescriptor[] stderr_read = ParcelFileDescriptor.createPipe();
+ stderr_source_read = stderr_read[0];
+ stderr_sink_read = stderr_read[1];
+ }
+
// Calling out without a lock held.
- mUiAutomationConnection.executeShellCommand(command, sink_read, source_write);
+ mUiAutomationConnection.executeShellCommandWithStderr(
+ command, sink_read, source_write, stderr_sink_read);
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error executing shell command!", ioe);
} catch (RemoteException re) {
@@ -1272,11 +1310,15 @@ public final class UiAutomation {
} finally {
IoUtils.closeQuietly(sink_read);
IoUtils.closeQuietly(source_write);
+ IoUtils.closeQuietly(stderr_sink_read);
}
- ParcelFileDescriptor[] result = new ParcelFileDescriptor[2];
+ ParcelFileDescriptor[] result = new ParcelFileDescriptor[includeStderr ? 3 : 2];
result[0] = source_read;
result[1] = sink_write;
+ if (includeStderr) {
+ result[2] = stderr_source_read;
+ }
return result;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 70d520176ca1..255b93f79811 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -372,6 +372,13 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
@Override
public void executeShellCommand(final String command, final ParcelFileDescriptor sink,
final ParcelFileDescriptor source) throws RemoteException {
+ executeShellCommandWithStderr(command, sink, source, null /* stderrSink */);
+ }
+
+ @Override
+ public void executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink,
+ final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)
+ throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
@@ -409,6 +416,18 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
writeToProcess = null;
}
+ // Read from process stderr and write to pipe
+ final Thread readStderrFromProcess;
+ if (stderrSink != null) {
+ InputStream sink_in = process.getErrorStream();
+ OutputStream sink_out = new FileOutputStream(stderrSink.getFileDescriptor());
+
+ readStderrFromProcess = new Thread(new Repeater(sink_in, sink_out));
+ readStderrFromProcess.start();
+ } else {
+ readStderrFromProcess = null;
+ }
+
Thread cleanup = new Thread(new Runnable() {
@Override
public void run() {
@@ -419,14 +438,18 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
if (readFromProcess != null) {
readFromProcess.join();
}
+ if (readStderrFromProcess != null) {
+ readStderrFromProcess.join();
+ }
} catch (InterruptedException exc) {
Log.e(TAG, "At least one of the threads was interrupted");
}
IoUtils.closeQuietly(sink);
IoUtils.closeQuietly(source);
+ IoUtils.closeQuietly(stderrSink);
process.destroy();
- }
- });
+ }
+ });
cleanup.start();
}